[clang] ffa214e - [Concepts] Constraint Enforcement & Diagnostics

Vlad Tsyrklevich via cfe-commits cfe-commits at lists.llvm.org
Mon Oct 28 15:02:35 PDT 2019


I’ve reverted this change as it causes the following tests to fail under
check-clang when built with ASAN:

    Clang :: CXX/expr/expr.prim/expr.prim.id/p3.cpp

    Clang :: CXX/temp/temp.constr/temp.constr.constr/function-templates.cpp

    Clang ::
CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp


You can see these failures here:
http://lab.llvm.org:8014/builders/sanitizer-x86_64-linux-bootstrap/builds/124/steps/check-clang%20asan/logs/stdio

On Thu, Oct 24, 2019 at 2:33 PM Saar Raz via cfe-commits <
cfe-commits at lists.llvm.org> wrote:

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


More information about the cfe-commits mailing list