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