[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