[Concepts] Concept Specialization Expressions

Part of C++20 Concepts implementation effort. Added Concept Specialization Expressions that are created when a concept is refe$

D41217 on Phabricator.

(recommit after fixing failing Parser test on windows)



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@374903 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h
index 61e7a91..57ccba8 100644
--- a/include/clang/AST/ExprCXX.h
+++ b/include/clang/AST/ExprCXX.h
@@ -17,6 +17,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DeclarationName.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/NestedNameSpecifier.h"
@@ -56,6 +57,7 @@
 class LambdaCapture;
 class NonTypeTemplateParmDecl;
 class TemplateParameterList;
+class Sema;
 
 //===--------------------------------------------------------------------===//
 // C++ Expressions.
@@ -4750,6 +4752,125 @@
   }
 };
 
+/// \brief Represents the specialization of a concept - evaluates to a prvalue
+/// of type bool.
+///
+/// According to C++2a [expr.prim.id]p3 an id-expression that denotes the
+/// specialization of a concept results in a prvalue of type bool.
+class ConceptSpecializationExpr final : public Expr,
+      private llvm::TrailingObjects<ConceptSpecializationExpr,
+                                    TemplateArgument> {
+  friend class ASTStmtReader;
+  friend TrailingObjects;
+
+  // \brief The optional nested name specifier used when naming the concept.
+  NestedNameSpecifierLoc NestedNameSpec;
+
+  /// \brief The location of the template keyword, if specified when naming the
+  /// concept.
+  SourceLocation TemplateKWLoc;
+
+  /// \brief The location of the concept name in the expression.
+  SourceLocation ConceptNameLoc;
+
+  /// \brief The declaration found by name lookup when the expression was
+  /// created.
+  /// Can differ from NamedConcept when, for example, the concept was found
+  /// through a UsingShadowDecl.
+  NamedDecl *FoundDecl;
+
+  /// \brief The concept named, and whether or not the concept with the given
+  /// arguments was satisfied when the expression was created.
+  /// If any of the template arguments are dependent (this expr would then be
+  /// isValueDependent()), this bit is to be ignored.
+  llvm::PointerIntPair<ConceptDecl *, 1, bool> NamedConcept;
+
+  /// \brief The template argument list source info used to specialize the
+  /// concept.
+  const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr;
+
+  /// \brief The number of template arguments in the tail-allocated list of
+  /// converted template arguments.
+  unsigned NumTemplateArgs;
+
+  ConceptSpecializationExpr(ASTContext &C, NestedNameSpecifierLoc NNS,
+                            SourceLocation TemplateKWLoc,
+                            SourceLocation ConceptNameLoc, NamedDecl *FoundDecl,
+                            ConceptDecl *NamedConcept,
+                            const ASTTemplateArgumentListInfo *ArgsAsWritten,
+                            ArrayRef<TemplateArgument> ConvertedArgs,
+                            Optional<bool> IsSatisfied);
+
+  ConceptSpecializationExpr(EmptyShell Empty, unsigned NumTemplateArgs);
+
+public:
+
+  static ConceptSpecializationExpr *
+  Create(ASTContext &C, NestedNameSpecifierLoc NNS,
+         SourceLocation TemplateKWLoc, SourceLocation ConceptNameLoc,
+         NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
+         const ASTTemplateArgumentListInfo *ArgsAsWritten,
+         ArrayRef<TemplateArgument> ConvertedArgs, Optional<bool> IsSatisfied);
+
+  static ConceptSpecializationExpr *
+  Create(ASTContext &C, EmptyShell Empty, unsigned NumTemplateArgs);
+
+  const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const {
+    return NestedNameSpec;
+  }
+
+  NamedDecl *getFoundDecl() const {
+    return FoundDecl;
+  }
+
+  ConceptDecl *getNamedConcept() const {
+    return NamedConcept.getPointer();
+  }
+
+  ArrayRef<TemplateArgument> getTemplateArguments() const {
+    return ArrayRef<TemplateArgument>(getTrailingObjects<TemplateArgument>(),
+                                      NumTemplateArgs);
+  }
+
+  const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const {
+    return ArgsAsWritten;
+  }
+
+  /// \brief Set new template arguments for this concept specialization.
+  void setTemplateArguments(const ASTTemplateArgumentListInfo *ArgsAsWritten,
+                            ArrayRef<TemplateArgument> Converted);
+
+  /// \brief Whether or not the concept with the given arguments was satisfied
+  /// when the expression was created. This method assumes that the expression
+  /// is not dependent!
+  bool isSatisfied() const {
+    assert(!isValueDependent()
+           && "isSatisfied called on a dependent ConceptSpecializationExpr");
+    return NamedConcept.getInt();
+  }
+
+  SourceLocation getConceptNameLoc() const { return ConceptNameLoc; }
+
+  SourceLocation getTemplateKWLoc() const { return TemplateKWLoc; }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == ConceptSpecializationExprClass;
+  }
+
+  SourceLocation getBeginLoc() const LLVM_READONLY { return ConceptNameLoc; }
+  SourceLocation getEndLoc() const LLVM_READONLY {
+    return ArgsAsWritten->RAngleLoc;
+  }
+
+  // Iterators
+  child_range children() {
+    return child_range(child_iterator(), child_iterator());
+  }
+  const_child_range children() const {
+    return const_child_range(const_child_iterator(), const_child_iterator());
+  }
+};
+
 } // namespace clang
 
 #endif // LLVM_CLANG_AST_EXPRCXX_H
diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h
index 09a6f78..998cf92 100644
--- a/include/clang/AST/RecursiveASTVisitor.h
+++ b/include/clang/AST/RecursiveASTVisitor.h
@@ -2659,6 +2659,12 @@
   }
 })
 
+DEF_TRAVERSE_STMT(ConceptSpecializationExpr, {
+  TRY_TO(TraverseTemplateArgumentLocsHelper(
+          S->getTemplateArgsAsWritten()->getTemplateArgs(),
+          S->getTemplateArgsAsWritten()->NumTemplateArgs));
+})
+
 // These literals (all of them) do not need any action.
 DEF_TRAVERSE_STMT(IntegerLiteral, {})
 DEF_TRAVERSE_STMT(FixedPointLiteral, {})
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 47a1dee..968c2db 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2526,8 +2526,6 @@
   "use __attribute__((visibility(\"hidden\"))) attribute instead">;
 
 // C++ Concepts
-def err_concept_initialized_with_non_bool_type : Error<
-  "constraint expression must be of type 'bool' but is of type %0">;
 def err_concept_decls_may_only_appear_in_global_namespace_scope : Error<
   "concept declarations may only appear in global or namespace scope">;
 def err_concept_no_parameters : Error<
@@ -2539,6 +2537,11 @@
   "concept cannot have associated constraints">;
 def err_concept_not_implemented : Error<
   "sorry, unimplemented concepts feature %0 used">;
+def err_non_constant_constraint_expression : Error<
+  "substitution into constraint expression resulted in a non-constant "
+  "expression">;
+def err_non_bool_atomic_constraint : Error<
+  "atomic constraint must be of type 'bool' (found %0)">;
 
 def err_template_different_associated_constraints : Error<
   "associated constraints differ in template redeclaration">;
@@ -4496,6 +4499,10 @@
   " template parameter%1 %2">;
 def note_template_default_arg_checking : Note<
   "while checking a default template argument used here">;
+def note_concept_specialization_here : Note<
+  "while checking the satisfaction of concept '%0' requested here">;
+def note_constraint_substitution_here : Note<
+  "while substituting template arguments into constraint expression here">;
 def note_instantiation_contexts_suppressed : Note<
   "(skipping %0 context%s0 in backtrace; use -ftemplate-backtrace-limit=0 to "
   "see all)">;
diff --git a/include/clang/Basic/StmtNodes.td b/include/clang/Basic/StmtNodes.td
index 1cef89f..6231484 100644
--- a/include/clang/Basic/StmtNodes.td
+++ b/include/clang/Basic/StmtNodes.td
@@ -163,6 +163,9 @@
 def DependentCoawaitExpr : DStmt<Expr>;
 def CoyieldExpr : DStmt<CoroutineSuspendExpr>;
 
+// C++2a Concepts expressions
+def ConceptSpecializationExpr : DStmt<Expr>;
+
 // Obj-C Expressions.
 def ObjCStringLiteral : DStmt<Expr>;
 def ObjCBoxedExpr : DStmt<Expr>;
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index 0403df4..6b2d603 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -6047,7 +6047,16 @@
                                            CXXConversionDecl *Conv,
                                            Expr *Src);
 
-  // ParseObjCStringLiteral - Parse Objective-C string literals.
+  /// Check whether the given expression is a valid constraint expression.
+  /// A diagnostic is emitted if it is not, and false is returned.
+  bool CheckConstraintExpression(Expr *CE);
+
+  bool CalculateConstraintSatisfaction(ConceptDecl *NamedConcept,
+                                       MultiLevelTemplateArgumentList &MLTAL,
+                                       Expr *ConstraintExpr,
+                                       bool &IsSatisfied);
+
+    // ParseObjCStringLiteral - Parse Objective-C string literals.
   ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs,
                                     ArrayRef<Expr *> Strings);
 
@@ -6718,9 +6727,9 @@
 
   ExprResult
   CheckConceptTemplateId(const CXXScopeSpec &SS,
-                         const DeclarationNameInfo &NameInfo,
-                         ConceptDecl *Template,
-                         SourceLocation TemplateLoc,
+                         SourceLocation TemplateKWLoc,
+                         SourceLocation ConceptNameLoc, NamedDecl *FoundDecl,
+                         ConceptDecl *NamedConcept,
                          const TemplateArgumentListInfo *TemplateArgs);
 
   void diagnoseMissingTemplateArguments(TemplateName Name, SourceLocation Loc);
@@ -7639,6 +7648,15 @@
       /// member).
       DefiningSynthesizedFunction,
 
+      // We are checking the constraints associated with a constrained entity or
+      // the constraint expression of a concept. This includes the checks that
+      // atomic constraints have the type 'bool' and that they can be constant
+      // evaluated.
+      ConstraintsCheck,
+
+      // We are substituting template arguments into a constraint expression.
+      ConstraintSubstitution,
+
       /// Added for Template instantiation observation.
       /// Memoization means we are _not_ instantiating a template because
       /// it is already instantiated (but we entered a context where we
@@ -7899,6 +7917,23 @@
                           ArrayRef<TemplateArgument> TemplateArgs,
                           SourceRange InstantiationRange);
 
+    struct ConstraintsCheck {};
+    /// \brief Note that we are checking the constraints associated with some
+    /// constrained entity (a concept declaration or a template with associated
+    /// constraints).
+    InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
+                          ConstraintsCheck, TemplateDecl *Template,
+                          ArrayRef<TemplateArgument> TemplateArgs,
+                          SourceRange InstantiationRange);
+
+    struct ConstraintSubstitution {};
+    /// \brief Note that we are checking a constraint expression associated
+    /// with a template declaration or as part of the satisfaction check of a
+    /// concept.
+    InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
+                          ConstraintSubstitution, TemplateDecl *Template,
+                          sema::TemplateDeductionInfo &DeductionInfo,
+                          SourceRange InstantiationRange);
 
     /// Note that we have finished instantiating this template.
     void Clear();
diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h
index eab1ec8..0d3971d 100644
--- a/include/clang/Serialization/ASTBitCodes.h
+++ b/include/clang/Serialization/ASTBitCodes.h
@@ -1915,6 +1915,7 @@
       EXPR_FUNCTION_PARM_PACK,    // FunctionParmPackExpr
       EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr
       EXPR_CXX_FOLD,              // CXXFoldExpr
+      EXPR_CONCEPT_SPECIALIZATION,// ConceptSpecializationExpr
 
       // CUDA
       EXPR_CUDA_KERNEL_CALL,       // CUDAKernelCallExpr
diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp
index 339f471..f2e6251 100644
--- a/lib/AST/Expr.cpp
+++ b/lib/AST/Expr.cpp
@@ -3403,6 +3403,7 @@
   case CXXUuidofExprClass:
   case OpaqueValueExprClass:
   case SourceLocExprClass:
+  case ConceptSpecializationExprClass:
     // These never have a side-effect.
     return false;
 
diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp
index cb6b870..7980049 100644
--- a/lib/AST/ExprCXX.cpp
+++ b/lib/AST/ExprCXX.cpp
@@ -28,6 +28,9 @@
 #include "clang/Basic/OperatorKinds.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/Specifiers.h"
+#include "clang/Sema/Template.h"
+#include "clang/Sema/SemaDiagnostic.h"
+#include "clang/Sema/Sema.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -1680,3 +1683,82 @@
                            alignof(CUDAKernelCallExpr));
   return new (Mem) CUDAKernelCallExpr(NumArgs, Empty);
 }
+
+ConceptSpecializationExpr::ConceptSpecializationExpr(ASTContext &C,
+    NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc,
+    SourceLocation ConceptNameLoc, NamedDecl *FoundDecl,
+    ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten,
+    ArrayRef<TemplateArgument> ConvertedArgs, Optional<bool> IsSatisfied)
+    : Expr(ConceptSpecializationExprClass, C.BoolTy, VK_RValue, OK_Ordinary,
+           /*TypeDependent=*/false,
+           // All the flags below are set in setTemplateArguments.
+           /*ValueDependent=*/!IsSatisfied.hasValue(),
+           /*InstantiationDependent=*/false,
+           /*ContainsUnexpandedParameterPacks=*/false),
+      NestedNameSpec(NNS), TemplateKWLoc(TemplateKWLoc),
+      ConceptNameLoc(ConceptNameLoc), FoundDecl(FoundDecl),
+      NamedConcept(NamedConcept, IsSatisfied ? *IsSatisfied : true),
+      NumTemplateArgs(ConvertedArgs.size()) {
+
+  setTemplateArguments(ArgsAsWritten, ConvertedArgs);
+}
+
+ConceptSpecializationExpr::ConceptSpecializationExpr(EmptyShell Empty,
+    unsigned NumTemplateArgs)
+    : Expr(ConceptSpecializationExprClass, Empty),
+      NumTemplateArgs(NumTemplateArgs) { }
+
+void ConceptSpecializationExpr::setTemplateArguments(
+    const ASTTemplateArgumentListInfo *ArgsAsWritten,
+    ArrayRef<TemplateArgument> Converted) {
+  assert(Converted.size() == NumTemplateArgs);
+  assert(!this->ArgsAsWritten && "setTemplateArguments can only be used once");
+  this->ArgsAsWritten = ArgsAsWritten;
+  std::uninitialized_copy(Converted.begin(), Converted.end(),
+                          getTrailingObjects<TemplateArgument>());
+  bool IsInstantiationDependent = false;
+  bool ContainsUnexpandedParameterPack = false;
+  for (const TemplateArgumentLoc& LocInfo : ArgsAsWritten->arguments()) {
+    if (LocInfo.getArgument().isInstantiationDependent())
+      IsInstantiationDependent = true;
+    if (LocInfo.getArgument().containsUnexpandedParameterPack())
+      ContainsUnexpandedParameterPack = true;
+    if (ContainsUnexpandedParameterPack && IsInstantiationDependent)
+      break;
+  }
+
+  // Currently guaranteed by the fact concepts can only be at namespace-scope.
+  assert(!NestedNameSpec ||
+         (!NestedNameSpec.getNestedNameSpecifier()->isInstantiationDependent() &&
+          !NestedNameSpec.getNestedNameSpecifier()
+              ->containsUnexpandedParameterPack()));
+  setInstantiationDependent(IsInstantiationDependent);
+  setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack);
+  assert((!isValueDependent() || isInstantiationDependent()) &&
+         "should not be value-dependent");
+}
+
+ConceptSpecializationExpr *
+ConceptSpecializationExpr::Create(ASTContext &C, NestedNameSpecifierLoc NNS,
+                                  SourceLocation TemplateKWLoc,
+                                  SourceLocation ConceptNameLoc,
+                                  NamedDecl *FoundDecl,
+                                  ConceptDecl *NamedConcept,
+                               const ASTTemplateArgumentListInfo *ArgsAsWritten,
+                                  ArrayRef<TemplateArgument> ConvertedArgs,
+                                  Optional<bool> IsSatisfied) {
+  void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>(
+                                ConvertedArgs.size()));
+  return new (Buffer) ConceptSpecializationExpr(C, NNS, TemplateKWLoc,
+                                                ConceptNameLoc, FoundDecl,
+                                                NamedConcept, ArgsAsWritten,
+                                                ConvertedArgs, IsSatisfied);
+}
+
+ConceptSpecializationExpr *
+ConceptSpecializationExpr::Create(ASTContext &C, EmptyShell Empty,
+                                  unsigned NumTemplateArgs) {
+  void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>(
+                                NumTemplateArgs));
+  return new (Buffer) ConceptSpecializationExpr(Empty, NumTemplateArgs);
+}
\ No newline at end of file
diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp
index c61ee70..d109cd3 100644
--- a/lib/AST/ExprClassification.cpp
+++ b/lib/AST/ExprClassification.cpp
@@ -192,6 +192,7 @@
   case Expr::NoInitExprClass:
   case Expr::DesignatedInitUpdateExprClass:
   case Expr::SourceLocExprClass:
+  case Expr::ConceptSpecializationExprClass:
     return Cl::CL_PRValue;
 
   case Expr::ConstantExprClass:
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index ceee50d..4459335 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -9768,6 +9768,7 @@
   bool VisitCXXNoexceptExpr(const CXXNoexceptExpr *E);
   bool VisitSizeOfPackExpr(const SizeOfPackExpr *E);
   bool VisitSourceLocExpr(const SourceLocExpr *E);
+  bool VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E);
   // FIXME: Missing: array subscript of vector, member of vector
 };
 
@@ -12250,6 +12251,12 @@
   return Success(E->getValue(), E);
 }
 
+bool IntExprEvaluator::VisitConceptSpecializationExpr(
+       const ConceptSpecializationExpr *E) {
+  return Success(E->isSatisfied(), E);
+}
+
+
 bool FixedPointExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) {
   switch (E->getOpcode()) {
     default:
@@ -13923,6 +13930,7 @@
   case Expr::CXXBoolLiteralExprClass:
   case Expr::CXXScalarValueInitExprClass:
   case Expr::TypeTraitExprClass:
+  case Expr::ConceptSpecializationExprClass:
   case Expr::ArrayTypeTraitExprClass:
   case Expr::ExpressionTraitExprClass:
   case Expr::CXXNoexceptExprClass:
diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp
index c6f7143..8e3ad4c 100644
--- a/lib/AST/ItaniumMangle.cpp
+++ b/lib/AST/ItaniumMangle.cpp
@@ -3658,6 +3658,7 @@
   case Expr::ConvertVectorExprClass:
   case Expr::StmtExprClass:
   case Expr::TypeTraitExprClass:
+  case Expr::ConceptSpecializationExprClass:
   case Expr::ArrayTypeTraitExprClass:
   case Expr::ExpressionTraitExprClass:
   case Expr::VAArgExprClass:
diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp
index e392682..70fe484 100644
--- a/lib/AST/StmtPrinter.cpp
+++ b/lib/AST/StmtPrinter.cpp
@@ -2231,6 +2231,17 @@
   OS << ")";
 }
 
+void StmtPrinter::VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) {
+  NestedNameSpecifierLoc NNS = E->getNestedNameSpecifierLoc();
+  if (NNS)
+    NNS.getNestedNameSpecifier()->print(OS, Policy);
+  if (E->getTemplateKWLoc().isValid())
+    OS << "template ";
+  OS << E->getFoundDecl()->getName();
+  printTemplateArgumentList(OS, E->getTemplateArgsAsWritten()->arguments(),
+                            Policy);
+}
+
 // C++ Coroutines TS
 
 void StmtPrinter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) {
diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp
index 9c19305..82bb4b8 100644
--- a/lib/AST/StmtProfile.cpp
+++ b/lib/AST/StmtProfile.cpp
@@ -1309,6 +1309,14 @@
   ID.AddInteger(S->getOp());
 }
 
+void StmtProfiler::VisitConceptSpecializationExpr(
+                                           const ConceptSpecializationExpr *S) {
+  VisitExpr(S);
+  VisitDecl(S->getFoundDecl());
+  VisitTemplateArguments(S->getTemplateArgsAsWritten()->getTemplateArgs(),
+                         S->getTemplateArgsAsWritten()->NumTemplateArgs);
+}
+
 static Stmt::StmtClass DecodeOperatorCall(const CXXOperatorCallExpr *S,
                                           UnaryOperatorKind &UnaryOp,
                                           BinaryOperatorKind &BinaryOp) {
diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp
index a10b18c..f139613 100644
--- a/lib/CodeGen/CGExprScalar.cpp
+++ b/lib/CodeGen/CGExprScalar.cpp
@@ -673,6 +673,10 @@
     return llvm::ConstantInt::get(ConvertType(E->getType()), E->getValue());
   }
 
+  Value *VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E) {
+    return Builder.getInt1(E->isSatisfied());
+  }
+
   Value *VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *E) {
     return llvm::ConstantInt::get(Builder.getInt32Ty(), E->getValue());
   }
diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp
index dc830cb..fea5826 100644
--- a/lib/Frontend/FrontendActions.cpp
+++ b/lib/Frontend/FrontendActions.cpp
@@ -417,6 +417,10 @@
       return "DefiningSynthesizedFunction";
     case CodeSynthesisContext::Memoization:
       return "Memoization";
+    case CodeSynthesisContext::ConstraintsCheck:
+      return "ConstraintsCheck";
+    case CodeSynthesisContext::ConstraintSubstitution:
+      return "ConstraintSubstitution";
     }
     return "";
   }
diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp
index 455d109..b74a95a 100644
--- a/lib/Parse/ParseExpr.cpp
+++ b/lib/Parse/ParseExpr.cpp
@@ -228,18 +228,16 @@
 /// Parse a constraint-expression.
 ///
 /// \verbatim
-///       constraint-expression: [Concepts TS temp.constr.decl p1]
+///       constraint-expression: C++2a[temp.constr.decl]p1
 ///         logical-or-expression
 /// \endverbatim
 ExprResult Parser::ParseConstraintExpression() {
-  // FIXME: this may erroneously consume a function-body as the braced
-  // initializer list of a compound literal
-  //
-  // FIXME: this may erroneously consume a parenthesized rvalue reference
-  // declarator as a parenthesized address-of-label expression
+  EnterExpressionEvaluationContext ConstantEvaluated(
+      Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
   ExprResult LHS(ParseCastExpression(/*isUnaryExpression=*/false));
   ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::LogicalOr));
-
+  if (Res.isUsable() && !Actions.CheckConstraintExpression(Res.get()))
+    return ExprError();
   return Res;
 }
 
diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt
index 7423435..89c3f6c 100644
--- a/lib/Sema/CMakeLists.txt
+++ b/lib/Sema/CMakeLists.txt
@@ -32,6 +32,7 @@
   SemaCast.cpp
   SemaChecking.cpp
   SemaCodeComplete.cpp
+  SemaConcept.cpp
   SemaConsumer.cpp
   SemaCoroutine.cpp
   SemaCUDA.cpp
diff --git a/lib/Sema/SemaConcept.cpp b/lib/Sema/SemaConcept.cpp
new file mode 100644
index 0000000..3131609
--- /dev/null
+++ b/lib/Sema/SemaConcept.cpp
@@ -0,0 +1,125 @@
+//===-- SemaConcept.cpp - Semantic Analysis for Constraints and Concepts --===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file implements semantic analysis for C++ constraints and concepts.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaDiagnostic.h"
+#include "clang/Sema/TemplateDeduction.h"
+#include "clang/Sema/Template.h"
+#include "clang/AST/ExprCXX.h"
+using namespace clang;
+using namespace sema;
+
+bool Sema::CheckConstraintExpression(Expr *ConstraintExpression) {
+  // C++2a [temp.constr.atomic]p1
+  // ..E shall be a constant expression of type bool.
+
+  ConstraintExpression = ConstraintExpression->IgnoreParenImpCasts();
+
+  if (auto *BinOp = dyn_cast<BinaryOperator>(ConstraintExpression)) {
+    if (BinOp->getOpcode() == BO_LAnd || BinOp->getOpcode() == BO_LOr)
+      return CheckConstraintExpression(BinOp->getLHS()) &&
+             CheckConstraintExpression(BinOp->getRHS());
+  } else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpression))
+    return CheckConstraintExpression(C->getSubExpr());
+
+  // An atomic constraint!
+  if (ConstraintExpression->isTypeDependent())
+    return true;
+
+  QualType Type = ConstraintExpression->getType();
+  if (!Context.hasSameUnqualifiedType(Type, Context.BoolTy)) {
+    Diag(ConstraintExpression->getExprLoc(),
+         diag::err_non_bool_atomic_constraint) << Type
+        << ConstraintExpression->getSourceRange();
+    return false;
+  }
+  return true;
+}
+
+bool
+Sema::CalculateConstraintSatisfaction(ConceptDecl *NamedConcept,
+                                      MultiLevelTemplateArgumentList &MLTAL,
+                                      Expr *ConstraintExpr,
+                                      bool &IsSatisfied) {
+  ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts();
+
+  if (auto *BO = dyn_cast<BinaryOperator>(ConstraintExpr)) {
+    if (BO->getOpcode() == BO_LAnd) {
+      if (CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getLHS(),
+                                          IsSatisfied))
+        return true;
+      if (!IsSatisfied)
+        return false;
+      return CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getRHS(),
+                                             IsSatisfied);
+    } else if (BO->getOpcode() == BO_LOr) {
+      if (CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getLHS(),
+                                          IsSatisfied))
+        return true;
+      if (IsSatisfied)
+        return false;
+      return CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getRHS(),
+                                             IsSatisfied);
+    }
+  }
+  else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr))
+    return CalculateConstraintSatisfaction(NamedConcept, MLTAL, C->getSubExpr(),
+                                           IsSatisfied);
+
+  EnterExpressionEvaluationContext ConstantEvaluated(
+      *this, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+
+  // Atomic constraint - substitute arguments and check satisfaction.
+  ExprResult E;
+  {
+    TemplateDeductionInfo Info(ConstraintExpr->getBeginLoc());
+    InstantiatingTemplate Inst(*this, ConstraintExpr->getBeginLoc(),
+                               InstantiatingTemplate::ConstraintSubstitution{},
+                               NamedConcept, Info,
+                               ConstraintExpr->getSourceRange());
+    if (Inst.isInvalid())
+      return true;
+    // We do not want error diagnostics escaping here.
+    Sema::SFINAETrap Trap(*this);
+
+    E = SubstExpr(ConstraintExpr, MLTAL);
+    if (E.isInvalid() || Trap.hasErrorOccurred()) {
+      // C++2a [temp.constr.atomic]p1
+      //   ...If substitution results in an invalid type or expression, the
+      //   constraint is not satisfied.
+      IsSatisfied = false;
+      return false;
+    }
+  }
+
+  if (!CheckConstraintExpression(E.get()))
+    return true;
+
+  SmallVector<PartialDiagnosticAt, 2> EvaluationDiags;
+  Expr::EvalResult EvalResult;
+  EvalResult.Diag = &EvaluationDiags;
+  if (!E.get()->EvaluateAsRValue(EvalResult, Context)) {
+    // C++2a [temp.constr.atomic]p1
+    //   ...E shall be a constant expression of type bool.
+    Diag(E.get()->getBeginLoc(),
+         diag::err_non_constant_constraint_expression)
+        << E.get()->getSourceRange();
+    for (const PartialDiagnosticAt &PDiag : EvaluationDiags)
+      Diag(PDiag.first, PDiag.second);
+    return true;
+  }
+
+  IsSatisfied = EvalResult.Val.getInt().getBoolValue();
+
+  return false;
+}
diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp
index 0f3a272..76fd10d 100644
--- a/lib/Sema/SemaExceptionSpec.cpp
+++ b/lib/Sema/SemaExceptionSpec.cpp
@@ -1314,6 +1314,7 @@
   case Expr::SizeOfPackExprClass:
   case Expr::StringLiteralClass:
   case Expr::SourceLocExprClass:
+  case Expr::ConceptSpecializationExprClass:
     // These expressions can never throw.
     return CT_Cannot;
 
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index 284962f..cb756eb 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -4271,14 +4271,47 @@
 
 ExprResult
 Sema::CheckConceptTemplateId(const CXXScopeSpec &SS,
-                             const DeclarationNameInfo &NameInfo,
-                             ConceptDecl *Template,
-                             SourceLocation TemplateLoc,
+                             SourceLocation TemplateKWLoc,
+                             SourceLocation ConceptNameLoc,
+                             NamedDecl *FoundDecl,
+                             ConceptDecl *NamedConcept,
                              const TemplateArgumentListInfo *TemplateArgs) {
-  // TODO: Do concept specialization here.
-  Diag(NameInfo.getBeginLoc(), diag::err_concept_not_implemented) <<
-    "concept specialization";
-  return ExprError();
+  assert(NamedConcept && "A concept template id without a template?");
+
+  llvm::SmallVector<TemplateArgument, 4> Converted;
+  if (CheckTemplateArgumentList(NamedConcept, ConceptNameLoc,
+                           const_cast<TemplateArgumentListInfo&>(*TemplateArgs),
+                                /*PartialTemplateArgs=*/false, Converted,
+                                /*UpdateArgsWithConversion=*/false))
+    return ExprError();
+
+  Optional<bool> IsSatisfied;
+  bool AreArgsDependent = false;
+  for (TemplateArgument &Arg : Converted) {
+    if (Arg.isDependent()) {
+      AreArgsDependent = true;
+      break;
+    }
+  }
+  if (!AreArgsDependent) {
+    InstantiatingTemplate Inst(*this, ConceptNameLoc,
+        InstantiatingTemplate::ConstraintsCheck{}, NamedConcept, Converted,
+        SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameLoc,
+                    TemplateArgs->getRAngleLoc()));
+    MultiLevelTemplateArgumentList MLTAL;
+    MLTAL.addOuterTemplateArguments(Converted);
+    bool Satisfied;
+    if (CalculateConstraintSatisfaction(NamedConcept, MLTAL,
+                                        NamedConcept->getConstraintExpr(),
+                                        Satisfied))
+      return ExprError();
+    IsSatisfied = Satisfied;
+  }
+  return ConceptSpecializationExpr::Create(Context,
+      SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc{},
+      TemplateKWLoc, ConceptNameLoc, FoundDecl, NamedConcept,
+      ASTTemplateArgumentListInfo::Create(Context, *TemplateArgs), Converted,
+      IsSatisfied);
 }
 
 ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
@@ -4322,9 +4355,10 @@
   }
 
   if (R.getAsSingle<ConceptDecl>() && !AnyDependentArguments()) {
-    return CheckConceptTemplateId(SS, R.getLookupNameInfo(),
-                                  R.getAsSingle<ConceptDecl>(),
-                                  TemplateKWLoc, TemplateArgs);
+    return CheckConceptTemplateId(SS, TemplateKWLoc,
+                                  R.getLookupNameInfo().getBeginLoc(),
+                                  R.getFoundDecl(),
+                                  R.getAsSingle<ConceptDecl>(), TemplateArgs);
   }
 
   // We don't want lookup warnings at this point.
@@ -8054,20 +8088,7 @@
   ConceptDecl *NewDecl = ConceptDecl::Create(Context, DC, NameLoc, Name,
                                              TemplateParameterLists.front(),
                                              ConstraintExpr);
-
-  if (!ConstraintExpr->isTypeDependent() &&
-      ConstraintExpr->getType() != Context.BoolTy) {
-    // C++2a [temp.constr.atomic]p3:
-    // E shall be a constant expression of type bool.
-    // TODO: Do this check for individual atomic constraints
-    // and not the constraint expression. Probably should do it in
-    // ParseConstraintExpression.
-    Diag(ConstraintExpr->getSourceRange().getBegin(),
-        diag::err_concept_initialized_with_non_bool_type)
-      << ConstraintExpr->getType();
-    NewDecl->setInvalidDecl();
-  }
-
+                                             
   if (NewDecl->getAssociatedConstraints()) {
     // C++2a [temp.concept]p4:
     // A concept shall not have associated constraints.
diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp
index 9091bc5..42411c9 100644
--- a/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/lib/Sema/SemaTemplateInstantiate.cpp
@@ -198,12 +198,14 @@
   case ExplicitTemplateArgumentSubstitution:
   case DeducedTemplateArgumentSubstitution:
   case PriorTemplateArgumentSubstitution:
+  case ConstraintsCheck:
     return true;
 
   case DefaultTemplateArgumentChecking:
   case DeclaringSpecialMember:
   case DefiningSynthesizedFunction:
   case ExceptionSpecEvaluation:
+  case ConstraintSubstitution:
     return false;
 
   // This function should never be called when Kind's value is Memoization.
@@ -358,6 +360,24 @@
           PointOfInstantiation, InstantiationRange, Param, Template,
           TemplateArgs) {}
 
+Sema::InstantiatingTemplate::InstantiatingTemplate(
+    Sema &SemaRef, SourceLocation PointOfInstantiation,
+    ConstraintsCheck, TemplateDecl *Template,
+    ArrayRef<TemplateArgument> TemplateArgs, SourceRange InstantiationRange)
+    : InstantiatingTemplate(
+          SemaRef, CodeSynthesisContext::ConstraintsCheck,
+          PointOfInstantiation, InstantiationRange, Template, nullptr,
+          TemplateArgs) {}
+
+Sema::InstantiatingTemplate::InstantiatingTemplate(
+    Sema &SemaRef, SourceLocation PointOfInstantiation,
+    ConstraintSubstitution, TemplateDecl *Template,
+    sema::TemplateDeductionInfo &DeductionInfo, SourceRange InstantiationRange)
+    : InstantiatingTemplate(
+          SemaRef, CodeSynthesisContext::ConstraintSubstitution,
+          PointOfInstantiation, InstantiationRange, Template, nullptr,
+          {}, &DeductionInfo) {}
+
 void Sema::pushCodeSynthesisContext(CodeSynthesisContext Ctx) {
   Ctx.SavedInNonInstantiationSFINAEContext = InNonInstantiationSFINAEContext;
   InNonInstantiationSFINAEContext = false;
@@ -664,6 +684,30 @@
 
     case CodeSynthesisContext::Memoization:
       break;
+    
+    case CodeSynthesisContext::ConstraintsCheck:
+      if (auto *CD = dyn_cast<ConceptDecl>(Active->Entity)) {
+        SmallVector<char, 128> TemplateArgsStr;
+        llvm::raw_svector_ostream OS(TemplateArgsStr);
+        CD->printName(OS);
+        printTemplateArgumentList(OS, Active->template_arguments(),
+                                  getPrintingPolicy());
+        Diags.Report(Active->PointOfInstantiation,
+                     diag::note_concept_specialization_here)
+          << OS.str()
+          << Active->InstantiationRange;
+        break;
+      }
+      // TODO: Concepts - implement this for constrained templates and partial
+      // specializations.
+      llvm_unreachable("only concept constraints are supported right now");
+      break;
+      
+    case CodeSynthesisContext::ConstraintSubstitution:
+      Diags.Report(Active->PointOfInstantiation,
+                   diag::note_constraint_substitution_here)
+          << Active->InstantiationRange;
+      break;
     }
   }
 }
@@ -687,6 +731,7 @@
       LLVM_FALLTHROUGH;
     case CodeSynthesisContext::DefaultFunctionArgumentInstantiation:
     case CodeSynthesisContext::ExceptionSpecInstantiation:
+    case CodeSynthesisContext::ConstraintsCheck:
       // This is a template instantiation, so there is no SFINAE.
       return None;
 
@@ -700,8 +745,10 @@
 
     case CodeSynthesisContext::ExplicitTemplateArgumentSubstitution:
     case CodeSynthesisContext::DeducedTemplateArgumentSubstitution:
-      // We're either substitution explicitly-specified template arguments
-      // or deduced template arguments, so SFINAE applies.
+    case CodeSynthesisContext::ConstraintSubstitution:
+      // We're either substituting explicitly-specified template arguments
+      // or deduced template arguments or a constraint expression, so SFINAE
+      // applies.
       assert(Active->DeductionInfo && "Missing deduction info pointer");
       return Active->DeductionInfo;
 
diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h
index 59cac78..8184d4c 100644
--- a/lib/Sema/TreeTransform.h
+++ b/lib/Sema/TreeTransform.h
@@ -3019,6 +3019,25 @@
   ///
   /// By default, performs semantic analysis to build the new expression.
   /// Subclasses may override this routine to provide different behavior.
+  ExprResult RebuildConceptSpecializationExpr(NestedNameSpecifierLoc NNS,
+      SourceLocation TemplateKWLoc, SourceLocation ConceptNameLoc,
+      NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
+      TemplateArgumentListInfo *TALI) {
+    CXXScopeSpec SS;
+    SS.Adopt(NNS);
+    ExprResult Result = getSema().CheckConceptTemplateId(SS, TemplateKWLoc,
+                                                         ConceptNameLoc,
+                                                         FoundDecl,
+                                                         NamedConcept, TALI);
+    if (Result.isInvalid())
+      return ExprError();
+    return Result;
+  }
+
+    /// \brief Build a new Objective-C boxed expression.
+  ///
+  /// By default, performs semantic analysis to build the new expression.
+  /// Subclasses may override this routine to provide different behavior.
   ExprResult RebuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) {
     return getSema().BuildObjCBoxedExpr(SR, ValueExpr);
   }
@@ -11016,6 +11035,23 @@
 
 template<typename Derived>
 ExprResult
+TreeTransform<Derived>::TransformConceptSpecializationExpr(
+                                                 ConceptSpecializationExpr *E) {
+  const ASTTemplateArgumentListInfo *Old = E->getTemplateArgsAsWritten();
+  TemplateArgumentListInfo TransArgs(Old->LAngleLoc, Old->RAngleLoc);
+  if (getDerived().TransformTemplateArguments(Old->getTemplateArgs(),
+                                              Old->NumTemplateArgs, TransArgs))
+    return ExprError();
+
+  return getDerived().RebuildConceptSpecializationExpr(
+      E->getNestedNameSpecifierLoc(), E->getTemplateKWLoc(),
+      E->getConceptNameLoc(), E->getFoundDecl(), E->getNamedConcept(),
+      &TransArgs);
+}
+
+
+template<typename Derived>
+ExprResult
 TreeTransform<Derived>::TransformArrayTypeTraitExpr(ArrayTypeTraitExpr *E) {
   TypeSourceInfo *T = getDerived().TransformType(E->getQueriedTypeSourceInfo());
   if (!T)
diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp
index 0c802a6..7eb56e1 100644
--- a/lib/Serialization/ASTReaderStmt.cpp
+++ b/lib/Serialization/ASTReaderStmt.cpp
@@ -734,6 +734,24 @@
   E->setRParenLoc(ReadSourceLocation());
 }
 
+void ASTStmtReader::VisitConceptSpecializationExpr(
+        ConceptSpecializationExpr *E) {
+  VisitExpr(E);
+  unsigned NumTemplateArgs = Record.readInt();
+  E->NestedNameSpec = Record.readNestedNameSpecifierLoc();
+  E->TemplateKWLoc = Record.readSourceLocation();
+  E->ConceptNameLoc = Record.readSourceLocation();
+  E->FoundDecl = ReadDeclAs<NamedDecl>();
+  E->NamedConcept.setPointer(ReadDeclAs<ConceptDecl>());
+  const ASTTemplateArgumentListInfo *ArgsAsWritten =
+      Record.readASTTemplateArgumentListInfo();
+  llvm::SmallVector<TemplateArgument, 4> Args;
+  for (unsigned I = 0; I < NumTemplateArgs; ++I)
+    Args.push_back(Record.readTemplateArgument());
+  E->setTemplateArguments(ArgsAsWritten, Args);
+  E->NamedConcept.setInt(Record.readInt() == 1);
+}
+
 void ASTStmtReader::VisitArraySubscriptExpr(ArraySubscriptExpr *E) {
   VisitExpr(E);
   E->setLHS(Record.readSubExpr());
@@ -3490,6 +3508,12 @@
     case EXPR_DEPENDENT_COAWAIT:
       S = new (Context) DependentCoawaitExpr(Empty);
       break;
+
+    case EXPR_CONCEPT_SPECIALIZATION:
+      unsigned numTemplateArgs = Record[ASTStmtReader::NumExprFields];
+      S = ConceptSpecializationExpr::Create(Context, Empty, numTemplateArgs);
+      break;
+      
     }
 
     // We hit a STMT_STOP, so we're done with this expression.
diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp
index 387ed44..deba1cc 100644
--- a/lib/Serialization/ASTWriterStmt.cpp
+++ b/lib/Serialization/ASTWriterStmt.cpp
@@ -388,6 +388,24 @@
   Code = serialization::EXPR_DEPENDENT_COAWAIT;
 }
 
+void ASTStmtWriter::VisitConceptSpecializationExpr(
+        ConceptSpecializationExpr *E) {
+  VisitExpr(E);
+  ArrayRef<TemplateArgument> TemplateArgs = E->getTemplateArguments();
+  Record.push_back(TemplateArgs.size());
+  Record.AddNestedNameSpecifierLoc(E->getNestedNameSpecifierLoc());
+  Record.AddSourceLocation(E->getTemplateKWLoc());
+  Record.AddSourceLocation(E->getConceptNameLoc());
+  Record.AddDeclRef(E->getFoundDecl());
+  Record.AddDeclRef(E->getNamedConcept());
+  Record.AddASTTemplateArgumentListInfo(E->getTemplateArgsAsWritten());
+  for (const TemplateArgument &Arg : TemplateArgs)
+    Record.AddTemplateArgument(Arg);
+  Record.push_back(E->isSatisfied());
+  Code = serialization::EXPR_CONCEPT_SPECIALIZATION;
+}
+
+
 void ASTStmtWriter::VisitCapturedStmt(CapturedStmt *S) {
   VisitStmt(S);
   // NumCaptures
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 855ecf1..8629fd9 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1370,6 +1370,7 @@
     case Stmt::CUDAKernelCallExprClass:
     case Stmt::OpaqueValueExprClass:
     case Stmt::AsTypeExprClass:
+    case Stmt::ConceptSpecializationExprClass:
       // Fall through.
 
     // Cases we intentionally don't evaluate, since they don't need
diff --git a/test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p1.cpp b/test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p1.cpp
deleted file mode 100644
index 863b608..0000000
--- a/test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p1.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-// RUN:  %clang_cc1 -std=c++14 -fconcepts-ts -fcxx-exceptions -x c++ -verify %s
-
-namespace A {
-  template<typename T> concept bool C1() { return true; }
-
-  template<typename T> concept bool C2 = true;
-}
-
-template<typename T> concept bool C3() { return (throw 0, true); }
-static_assert(noexcept(C3<int>()), "function concept should be treated as if noexcept(true) specified");
-
-template<typename T> concept bool D1(); // expected-error {{function concept declaration must be a definition}}
-
-struct B {
-  template<typename T> concept bool D2() { return true; } // expected-error {{concept declarations may only appear in namespace scope}}
-};
-
-struct C {
-  template<typename T> static concept bool D3 = true; // expected-error {{concept declarations may only appear in namespace scope}}
-};
-
-concept bool D4() { return true; } // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
-
-concept bool D5 = true; // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
-
-template<typename T>
-concept bool D6; // expected-error {{variable concept declaration must be initialized}}
-
-template<typename T>
-concept bool D7() throw(int) { return true; } // expected-error {{function concept cannot have exception specification}}
-
-// Tag
-
-concept class CC1 {}; // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
-concept struct CS1 {}; // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
-concept union CU1 {}; // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
-concept enum CE1 {}; // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
-template <typename T> concept class TCC1 {}; // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
-template <typename T> concept struct TCS1 {}; // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
-template <typename T> concept union TCU1 {}; // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
-typedef concept int CI; // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
-void fpc(concept int i) {} // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
-
-concept bool; // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
-
-template <typename T> concept bool VCEI{ true };
-template concept bool VCEI<int>; // expected-error {{'concept' cannot be applied on an explicit instantiation}}
-extern template concept bool VCEI<int>; // expected-error {{'concept' cannot be applied on an explicit instantiation}}
-
-template <typename T> concept bool VCPS{ true };
-template <typename T> concept bool VCPS<T *>{ true }; // expected-error {{'concept' cannot be applied on an partial specialization}}
-
-template <typename T> concept bool VCES{ true };
-template <> concept bool VCES<int>{ true }; // expected-error {{'concept' cannot be applied on an explicit specialization}}
-
-template <typename T> concept bool FCEI() { return true; }
-template concept bool FCEI<int>(); // expected-error {{'concept' cannot be applied on an explicit instantiation}}
-extern template concept bool FCEI<int>(); // expected-error {{'concept' cannot be applied on an explicit instantiation}}
-
-template <typename T> concept bool FCES() { return true; }
-template <> concept bool FCES<bool>() { return true; } // expected-error {{'concept' cannot be applied on an explicit specialization}}
diff --git a/test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p2.cpp b/test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p2.cpp
deleted file mode 100644
index 4779109..0000000
--- a/test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p2.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-// RUN:  %clang_cc1 -std=c++14 -fconcepts-ts -x c++ -verify %s
-
-template<typename T> concept thread_local bool VCTL = true; // expected-error {{variable concept cannot be declared 'thread_local'}}
-
-template<typename T> concept constexpr bool VCC = true; // expected-error {{variable concept cannot be declared 'constexpr'}}
-
-template<typename T> concept inline bool FCI() { return true; } // expected-error {{function concept cannot be declared 'inline'}}
-
-struct X {
-  template<typename T> concept friend bool FCF() { return true; } // expected-error {{function concept cannot be declared 'friend'}}
-};
-
-template<typename T> concept constexpr bool FCC() { return true; } // expected-error {{function concept cannot be declared 'constexpr'}}
diff --git a/test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p5.cpp b/test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p5.cpp
deleted file mode 100644
index 69672ca..0000000
--- a/test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p5.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-// RUN:  %clang_cc1 -std=c++14 -fconcepts-ts -x c++ -verify %s
-
-template<typename T>
-concept bool fcpv(void) { return true; }
-
-template<typename T>
-concept bool fcpi(int i = 0) { return true; } // expected-error {{function concept cannot have any parameters}}
-
-template<typename... Ts>
-concept bool fcpp(Ts... ts) { return true; } // expected-error {{function concept cannot have any parameters}}
-
-template<typename T>
-concept bool fcpva(...) { return true; } // expected-error {{function concept cannot have any parameters}}
-
-template<typename T>
-concept const bool fcrtc() { return true; } // expected-error {{declared return type of function concept must be 'bool'}}
-
-template<typename T>
-concept int fcrti() { return 5; } // expected-error {{declared return type of function concept must be 'bool'}}
-
-template<typename T>
-concept float fcrtf() { return 5.5; } // expected-error {{declared return type of function concept must be 'bool'}}
-
-template<typename T>
-concept decltype(auto) fcrtd(void) { return true; } // expected-error {{declared return type of function concept must be 'bool'}}
diff --git a/test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p6.cpp b/test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p6.cpp
deleted file mode 100644
index f8a1bb7..0000000
--- a/test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p6.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-// RUN:  %clang_cc1 -std=c++14 -fconcepts-ts -x c++ -verify %s
-
-template<typename T>
-concept bool vc { true };
-
-template<typename T>
-struct B { typedef bool Boolean; };
-
-template<int N>
-B<void>::Boolean concept vctb(!0);
-
-template<typename T>
-concept const bool vctc { true }; // expected-error {{declared type of variable concept must be 'bool'}}
-
-template<typename T>
-concept int vcti { 5 }; // expected-error {{declared type of variable concept must be 'bool'}}
-
-template<typename T>
-concept float vctf { 5.5 }; // expected-error {{declared type of variable concept must be 'bool'}}
-
-template<typename T>
-concept auto vcta { true }; // expected-error {{declared type of variable concept must be 'bool'}}
-
-template<typename T>
-concept decltype(auto) vctd { true }; // expected-error {{declared type of variable concept must be 'bool'}}
diff --git a/test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p7.cpp b/test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p7.cpp
deleted file mode 100644
index 1bad6bb..0000000
--- a/test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p7.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-// RUN:  %clang_cc1 -std=c++14 -fconcepts-ts -x c++ -verify %s
-
-template <typename T> concept bool FCEI() { return true; } // expected-note {{previous declaration is here}} expected-note {{previous declaration is here}}
-template bool FCEI<int>(); // expected-error {{function concept cannot be explicitly instantiated}}
-extern template bool FCEI<double>(); // expected-error {{function concept cannot be explicitly instantiated}}
-
-template <typename T> concept bool FCES() { return true; } // expected-note {{previous declaration is here}}
-template <> bool FCES<int>() { return true; } // expected-error {{function concept cannot be explicitly specialized}}
-
-template <typename T> concept bool VC { true }; // expected-note {{previous declaration is here}} expected-note {{previous declaration is here}}
-template bool VC<int>; // expected-error {{variable concept cannot be explicitly instantiated}}
-extern template bool VC<double>; // expected-error {{variable concept cannot be explicitly instantiated}}
-
-template <typename T> concept bool VCES { true }; // expected-note {{previous declaration is here}}
-template <> bool VCES<int> { true }; // expected-error {{variable concept cannot be explicitly specialized}}
-
-template <typename T> concept bool VCPS { true }; // expected-note {{previous declaration is here}}
-template <typename T> bool VCPS<T *> { true }; // expected-error {{variable concept cannot be partially specialized}}
diff --git a/test/CXX/concepts-ts/dcl.dcl/lit.cfg.py b/test/CXX/concepts-ts/dcl.dcl/lit.cfg.py
deleted file mode 100644
index c705e3c..0000000
--- a/test/CXX/concepts-ts/dcl.dcl/lit.cfg.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- Python -*-
-
-import os
-import lit.formats
-
-from lit.llvm import llvm_config
-
-# Configuration file for the 'lit' test runner.
-
-# name: The name of this test suite.
-config.name = 'Clang-Concepts-TS-Unsupported'
-
-# testFormat: The test format to use to interpret tests.
-#
-# For now we require '&&' between commands, until they get globally killed and
-# the test runner updated.
-config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell)
-
-# suffixes: A list of file extensions to treat as test files.
-config.suffixes = ['.c', '.cpp', '.cppm', '.m', '.mm', '.cu',
-                   '.ll', '.cl', '.s', '.S', '.modulemap', '.test', '.rs']
-
-config.unsupported = True
-
-# test_source_root: The root path where tests are located.
-config.test_source_root = os.path.dirname(__file__)
diff --git a/test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp b/test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp
deleted file mode 100644
index 16da146..0000000
--- a/test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-// RUN:  %clang_cc1 -std=c++2a -fconcepts-ts -verify %s
-
-template<typename T> concept C = true;
-static_assert(C<int>); // expected-error{{sorry, unimplemented concepts feature concept specialization used}}
diff --git a/test/CXX/expr/expr.prim/expr.prim.id/p3.cpp b/test/CXX/expr/expr.prim/expr.prim.id/p3.cpp
new file mode 100644
index 0000000..dd3f0c0
--- /dev/null
+++ b/test/CXX/expr/expr.prim/expr.prim.id/p3.cpp
@@ -0,0 +1,149 @@
+// RUN:  %clang_cc1 -std=c++2a -verify -triple x86_64-linux-gnu %s
+
+template<typename T> concept C1 = true; // expected-note{{template is declared here}}
+static_assert(C1<int>);
+static_assert(C1);
+// expected-error@-1{{use of concept 'C1' requires template arguments}}
+
+template<typename T> concept C2 = sizeof(T) == 4;
+static_assert(C2<int>);
+static_assert(!C2<long long int>);
+static_assert(C2<char[4]>);
+static_assert(!C2<char[5]>);
+
+template<typename T> concept C3 = sizeof(*T{}) == 4;
+static_assert(C3<int*>);
+static_assert(!C3<long long int>);
+
+struct A {
+  static constexpr int add(int a, int b) {
+    return a + b;
+  }
+};
+struct B {
+  static int add(int a, int b) { // expected-note{{declared here}}
+    return a + b;
+  }
+};
+template<typename U>
+concept C4 = U::add(1, 2) == 3;
+// expected-error@-1{{substitution into constraint expression resulted in a non-constant expression}}
+// expected-note@-2{{non-constexpr function 'add' cannot be used in a constant expression}}
+static_assert(C4<A>);
+static_assert(!C4<B>); // expected-note {{while checking the satisfaction of concept 'C4<B>' requested here}}
+
+template<typename T, typename U>
+constexpr bool is_same_v = false;
+
+template<typename T>
+constexpr bool is_same_v<T, T> = true;
+
+template<typename T, typename U>
+concept Same = is_same_v<T, U>;
+
+static_assert(Same<int, int>);
+static_assert(Same<int, decltype(1)>);
+static_assert(!Same<int, unsigned int>);
+static_assert(!Same<A, B>);
+static_assert(Same<A, A>);
+
+static_assert(Same<bool, decltype(C1<int>)>);
+static_assert(Same<bool, decltype(C2<int>)>);
+static_assert(Same<bool, decltype(C3<int*>)>);
+static_assert(Same<bool, decltype(C4<A>)>);
+
+template<typename T> concept C5 = T{}; // expected-error {{atomic constraint must be of type 'bool' (found 'int')}}
+constexpr bool x = C5<int>; // expected-note {{while checking the satisfaction of concept 'C5<int>' requested here}}
+
+template<int x>
+concept IsEven = (x % 2) == 0;
+
+static_assert(IsEven<20>);
+static_assert(!IsEven<11>);
+
+template<template<typename T> typename P>
+concept IsTypePredicate = is_same_v<decltype(P<bool>::value), const bool>
+                          && is_same_v<decltype(P<int>::value), const bool>
+                          && is_same_v<decltype(P<long long>::value), const bool>;
+
+template<typename T> struct T1 {};
+template<typename T> struct T2 { static constexpr bool value = sizeof(T) == 2; };
+
+static_assert(IsTypePredicate<T2>);
+static_assert(!IsTypePredicate<T1>);
+
+namespace piecewise_substitution {
+  template <typename T>
+  concept True = true;
+
+  template <typename T>
+  concept A = True<T> || T::value;
+
+  template <typename T>
+  concept B = (True<T> || T::value);
+
+  template <typename T>
+  concept C = !True<T> && T::value || true;
+
+  template <typename T>
+  concept D = (!True<T> && T::value) || true;
+
+  template <typename T>
+  concept E = T::value || True<T>;
+
+  template <typename T>
+  concept F = (T::value || True<T>);
+
+  template <typename T>
+  concept G = T::value && !True<T> || true;
+
+  template <typename T>
+  concept H = (T::value && !True<T>) || true;
+
+  template <typename T>
+  concept I = T::value;
+
+  static_assert(A<int>);
+  static_assert(B<int>);
+  static_assert(C<int>);
+  static_assert(D<int>);
+  static_assert(E<int>);
+  static_assert(F<int>);
+  static_assert(G<int>);
+  static_assert(H<int>);
+  static_assert(!I<int>);
+}
+
+// Short ciruiting
+
+template<typename T> struct T3 { using type = typename T::type; };
+// expected-error@-1{{type 'char' cannot be used prior to '::' because it has no members}}
+// expected-error@-2{{type 'short' cannot be used prior to '::' because it has no members}}
+
+template<typename T>
+concept C6 = sizeof(T) == 1 && sizeof(typename T3<T>::type) == 1;
+// expected-note@-1{{while substituting template arguments into constraint expression here}}
+// expected-note@-2{{in instantiation of template class 'T3<char>' requested here}}
+
+template<typename T>
+concept C7 = sizeof(T) == 1 || sizeof(
+// expected-note@-1{{while substituting template arguments into constraint expression here}}
+    typename
+      T3<T>
+// expected-note@-1{{in instantiation of template class 'T3<short>' requested here}}
+        ::type) == 1;
+
+static_assert(!C6<short>);
+static_assert(!C6<char>); // expected-note{{while checking the satisfaction of concept 'C6<char>' requested here}}
+static_assert(C7<char>);
+static_assert(!C7<short>); // expected-note{{while checking the satisfaction of concept 'C7<short>' requested here}}
+
+// Make sure argument list is converted when instantiating a CSE.
+
+template<typename T, typename U = int>
+concept SameSize = sizeof(T) == sizeof(U);
+
+template<typename T>
+struct X { static constexpr bool a = SameSize<T>; };
+
+static_assert(X<unsigned>::a);
diff --git a/test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/class-template-decl.cpp b/test/CXX/temp/temp.constr/temp.constr.decl/class-template-decl.cpp
similarity index 100%
rename from test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/class-template-decl.cpp
rename to test/CXX/temp/temp.constr/temp.constr.decl/class-template-decl.cpp
diff --git a/test/PCH/cxx2a-concept-specialization-expr.cpp b/test/PCH/cxx2a-concept-specialization-expr.cpp
new file mode 100644
index 0000000..6227d57
--- /dev/null
+++ b/test/PCH/cxx2a-concept-specialization-expr.cpp
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -std=c++2a -emit-pch %s -o %t
+// RUN: %clang_cc1 -std=c++2a -include-pch %t -verify %s
+
+// expected-no-diagnostics
+
+#ifndef HEADER
+#define HEADER
+
+template<typename... T>
+concept C = true;
+
+namespace n {
+  template<typename... T>
+  concept C = true;
+}
+
+void f() {
+  (void)C<int>;
+  (void)C<int, void>;
+  (void)n::C<void>;
+}
+
+#else /*included pch*/
+
+int main() {
+  (void)C<int>;
+  (void)C<int, void>;
+  (void)n::C<void>;
+  f();
+}
+
+#endif // HEADER
diff --git a/test/Parser/cxx2a-concept-declaration.cpp b/test/Parser/cxx2a-concept-declaration.cpp
index d80b3db..a236252 100644
--- a/test/Parser/cxx2a-concept-declaration.cpp
+++ b/test/Parser/cxx2a-concept-declaration.cpp
@@ -14,8 +14,6 @@
 // expected-error@-2{{template template parameter requires 'class' after the parameter list}}
 // expected-error@-3{{concept template parameter list must have at least one parameter; explicit specialization of concepts is not allowed}}
 
-template<typename T> concept C2 = 0.f; // expected-error {{constraint expression must be of type 'bool' but is of type 'float'}}
-
 struct S1 {
   template<typename T> concept C1 = true; // expected-error {{concept declarations may only appear in global or namespace scope}}
 };
@@ -26,15 +24,15 @@
 
 template<typename A>
 template<typename B>
-concept C4 = true; // expected-error {{extraneous template parameter list in concept definition}}
+concept C2 = true; // expected-error {{extraneous template parameter list in concept definition}}
 
-template<typename T> concept C5 = true; // expected-note {{previous}} expected-note {{previous}}
-int C5; // expected-error {{redefinition}}
-struct C5 {}; // expected-error {{redefinition}}
+template<typename T> concept C3 = true; // expected-note {{previous}} expected-note {{previous}}
+int C3; // expected-error {{redefinition}}
+struct C3 {}; // expected-error {{redefinition}}
 
-struct C6 {}; // expected-note{{previous definition is here}}
-template<typename T> concept C6 = true;
-// expected-error@-1{{redefinition of 'C6' as different kind of symbol}}
+struct C4 {}; // expected-note{{previous definition is here}}
+template<typename T> concept C4 = true;
+// expected-error@-1{{redefinition of 'C4' as different kind of symbol}}
 
 // TODO: Add test to prevent explicit specialization, partial specialization
 // and explicit instantiation of concepts.
@@ -43,31 +41,60 @@
 struct integral_constant { static constexpr T value = v; };
 
 namespace N {
-  template<typename T> concept C7 = true;
+  template<typename T> concept C5 = true;
 }
-using N::C7;
+using N::C5;
 
-template <bool word> concept C8 = integral_constant<bool, wor>::value;
+template <bool word> concept C6 = integral_constant<bool, wor>::value;
 // expected-error@-1{{use of undeclared identifier 'wor'; did you mean 'word'?}}
 // expected-note@-2{{'word' declared here}}
 
-template<typename T> concept bool C9 = true;
+template<typename T> concept bool C7 = true;
 // expected-warning@-1{{ISO C++2a does not permit the 'bool' keyword after 'concept'}}
 
-template<> concept C10 = false;
+template<> concept C8 = false;
 // expected-error@-1{{concept template parameter list must have at least one parameter; explicit specialization of concepts is not allowed}}
 
-template<> concept C9<int> = false;
+template<> concept C7<int> = false;
 // expected-error@-1{{name defined in concept definition must be an identifier}}
 
-template<typename T> concept N::C11 = false;
+template<typename T> concept N::C9 = false;
 // expected-error@-1{{name defined in concept definition must be an identifier}}
 
 class A { };
 // expected-note@-1{{'A' declared here}}
 
-template<typename T> concept A::C12 = false;
+template<typename T> concept A::C10 = false;
 // expected-error@-1{{expected namespace name}}
 
 template<typename T> concept operator int = false;
 // expected-error@-1{{name defined in concept definition must be an identifier}}
+
+template<bool x> concept C11 = 2; // expected-error {{atomic constraint must be of type 'bool' (found 'int')}}
+template<bool x> concept C12 = 2 && x; // expected-error {{atomic constraint must be of type 'bool' (found 'int')}}
+template<bool x> concept C13 = x || 2 || x; // expected-error {{atomic constraint must be of type 'bool' (found 'int')}}
+template<bool x> concept C14 = 8ull && x || x; // expected-error {{atomic constraint must be of type 'bool' (found 'unsigned long long')}}
+template<typename T> concept C15 = sizeof(T); // expected-error {{atomic constraint must be of type 'bool'}}
+template<typename T> concept C16 = true && (0 && 0); // expected-error {{atomic constraint must be of type 'bool' (found 'int')}}
+// expected-warning@-1{{use of logical '&&' with constant operand}}
+// expected-note@-2{{use '&' for a bitwise operation}}
+// expected-note@-3{{remove constant to silence this warning}}
+template<typename T> concept C17 = T{};
+static_assert(!C17<bool>);
+template<typename T> concept C18 = (bool&&)true;
+static_assert(C18<int>);
+template<typename T> concept C19 = (const bool&)true;
+static_assert(C19<int>);
+template<typename T> concept C20 = (const bool)true;
+static_assert(C20<int>);
+template <bool c> concept C21 = integral_constant<bool, c>::value && true;
+static_assert(C21<true>);
+static_assert(!C21<false>);
+template <bool c> concept C22 = integral_constant<bool, c>::value;
+static_assert(C22<true>);
+static_assert(!C22<false>);
+
+template <bool word> concept C23 = integral_constant<bool, wor>::value;
+// expected-error@-1{{use of undeclared identifier 'wor'; did you mean 'word'?}}
+// expected-note@-2{{'word' declared here}}
+
diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp
index 93e588e..634b108 100644
--- a/tools/libclang/CXCursor.cpp
+++ b/tools/libclang/CXCursor.cpp
@@ -256,6 +256,7 @@
   case Stmt::BinaryConditionalOperatorClass:
   case Stmt::TypeTraitExprClass:
   case Stmt::CoawaitExprClass:
+  case Stmt::ConceptSpecializationExprClass:
   case Stmt::DependentCoawaitExprClass:
   case Stmt::CoyieldExprClass:
   case Stmt::CXXBindTemporaryExprClass: