saar.raz created this revision.
saar.raz added reviewers: hubert.reinterpretcast, rsmith, nwilson, changyu, 
faisalv.
Herald added a subscriber: cfe-commits.

- 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.

This patch depends upon https://reviews.llvm.org/D41284


Repository:
  rC Clang

https://reviews.llvm.org/D41569

Files:
  include/clang/AST/ExprCXX.h
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  include/clang/Sema/TemplateDeduction.h
  lib/AST/ExprCXX.cpp
  lib/Sema/SemaOverload.cpp
  lib/Sema/SemaTemplate.cpp
  lib/Sema/SemaTemplateDeduction.cpp
  lib/Sema/SemaTemplateInstantiateDecl.cpp
  lib/Serialization/ASTReaderStmt.cpp
  lib/Serialization/ASTWriterStmt.cpp
  test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp
  
test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/function-templates.cpp
  
test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/non-function-templates.cpp
  
test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/partial-specializations.cpp

Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/partial-specializations.cpp
===================================================================
--- /dev/null
+++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/partial-specializations.cpp
@@ -0,0 +1,28 @@
+// 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 {{'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], because:}}
+}
+
+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*>);
+}
\ No newline at end of file
Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/non-function-templates.cpp
===================================================================
--- /dev/null
+++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/non-function-templates.cpp
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s
+
+template<typename T> requires sizeof(T) >= 2 // expected-note{{'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], because:}}
+
+template<typename T, typename U>
+  requires sizeof(T) != sizeof(U) // expected-note{{'sizeof(int) != sizeof(char [4])' (4 != 4) evaluated to false}}
+           && sizeof(T) >= 4 // expected-note{{'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]], because:}}
+static_assert(SizeDiff<char, int> == 3); // expected-error{{constraints not satisfied for variable template 'SizeDiff' [with T = char, U = int], because:}}
+
+template<typename... Ts>
+  requires ((sizeof(Ts) == 4) || ...) // expected-note{{'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]>], because:}}
+
+template<typename T>
+concept IsBig = sizeof(T) > 100; // expected-note{{'sizeof(int) > 100' (4 > 100) evaluated to false}}
+
+template<typename T>
+  requires IsBig<T> // expected-note{{'int' does not satisfy 'IsBig', because:}}
+using BigPtr = T*;
+
+static_assert(sizeof(BigPtr<int>)); // expected-error{{constraints not satisfied for alias template 'BigPtr' [with T = int], because:}}}}
+
+template<typename T> requires T::value // expected-note{{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], because:}}
+static_assert(S<S2>::value);
\ No newline at end of file
Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/function-templates.cpp
===================================================================
--- /dev/null
+++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/function-templates.cpp
@@ -0,0 +1,32 @@
+// 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 {{'is_ptr_v<int>' evaluated to false}} expected-note {{'is_ptr_v<char>' evaluated to false}}
+auto dereference(T t) { // expected-note {{candidate template ignored: constraints not satisfied [with T = int]}} expected-note {{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 {{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;
+}
+
+struct A { };
+
+static_assert(foo(A{})); // expected-error {{no matching function for call to 'foo'}}
\ No newline at end of file
Index: test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp
===================================================================
--- test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp
+++ test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp
@@ -24,9 +24,9 @@
     }
 };
 template<typename U>
-concept C4 = U::add(1, 2) == 3;
+concept C4 = U::add(1, 2) == 3; // expected-error {{substitution into constraint expression resulted in a non-constant expression 'B::add(1, 2) == 3'}}
 static_assert(C4<A>);
-static_assert(!C4<B>); // expected-error {{concept specialization 'C4<B>' resulted in a non-constant expression 'B::add(1, 2) == 3'}}
+static_assert(!C4<B>); // expected-note {{in concept specialization 'C4<B>'}}
 
 template<typename T, typename U>
 constexpr bool is_same_v = false;
Index: lib/Serialization/ASTWriterStmt.cpp
===================================================================
--- lib/Serialization/ASTWriterStmt.cpp
+++ lib/Serialization/ASTWriterStmt.cpp
@@ -357,6 +357,7 @@
     Record.AddTemplateArgumentLoc(TALI->getArgumentArray()[i]);
   }
   Record.push_back(E->isSatisfied());
+  Record.AddStmt(E->getSubstitutedConstraintExpr());
   Code = serialization::EXPR_CONCEPT_SPECIALIZATION;
 }
 
Index: lib/Serialization/ASTReaderStmt.cpp
===================================================================
--- lib/Serialization/ASTReaderStmt.cpp
+++ lib/Serialization/ASTReaderStmt.cpp
@@ -624,6 +624,7 @@
     ArgInfo.addArgument(Record.readTemplateArgumentLoc());
   E->setTemplateArguments(/*Sema=*/nullptr, &ArgInfo);
   E->setSatisfied(Record.readInt() == 1);
+  E->setSubstitutedConstraintExpr(Record.readExpr());
 }
 
 void ASTStmtReader::VisitArraySubscriptExpr(ArraySubscriptExpr *E) {
Index: lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2911,6 +2911,12 @@
                                         Converted))
     return nullptr;
 
+  if (SemaRef.EnsureTemplateArgumentListConstraints(InstClassTemplate,
+                                                    Converted,
+                                                    D->getLocation())) {
+    return nullptr;
+  }
+
   // Figure out where to insert this class template explicit specialization
   // in the member template's set of class template explicit specializations.
   void *InsertPos = nullptr;
@@ -3032,6 +3038,11 @@
           Converted))
     return nullptr;
 
+  if (SemaRef.EnsureTemplateArgumentListConstraints(VarTemplate, Converted,
+                                                   VarTemplate->getLocStart())){
+    return nullptr;
+  }
+
   // Find the variable template specialization declaration that
   // corresponds to these arguments.
   void *InsertPos = nullptr;
Index: lib/Sema/SemaTemplateDeduction.cpp
===================================================================
--- lib/Sema/SemaTemplateDeduction.cpp
+++ lib/Sema/SemaTemplateDeduction.cpp
@@ -2658,6 +2658,27 @@
   return Sema::TDK_Success;
 }
 
+template<typename TemplateDeclT>
+static Sema::TemplateDeductionResult
+CheckDeducedArgumentConstraints(Sema& S, TemplateDeclT *Template,
+                                ArrayRef<TemplateArgument> DeducedArgs,
+                                TemplateDeductionInfo& Info) {
+  bool IsSatisfied = false;
+  ExprResult SubstitutedExpr;
+  llvm::Optional<PartialDiagnosticAt> SubstDiag;
+  if (S.CheckConstraintSatisfaction(Template,
+                                    Template->getAssociatedConstraints(),
+                                    DeducedArgs, Info.getLocation(),
+                                    IsSatisfied, SubstitutedExpr, SubstDiag)
+      || !IsSatisfied) {
+    if (SubstDiag)
+      Info.addSFINAEDiagnostic(SubstDiag->first, SubstDiag->second);
+    Info.reset(TemplateArgumentList::CreateCopy(S.Context, DeducedArgs));
+    Info.SubstitutedConstraintExpr = SubstitutedExpr;
+    return Sema::TDK_ConstraintsNotSatisfied;
+  }
+  return Sema::TDK_Success;
+}
 
 /// \brief Perform template argument deduction to determine whether
 /// the given template arguments match the given class template
@@ -2698,6 +2719,10 @@
   if (Trap.hasErrorOccurred())
     return Sema::TDK_SubstitutionFailure;
 
+  if (TemplateDeductionResult Result
+        = CheckDeducedArgumentConstraints(*this, Partial, DeducedArgs, Info))
+    return Result;
+
   return ::FinishTemplateArgumentDeduction(
       *this, Partial, /*PartialOrdering=*/false, TemplateArgs, Deduced, Info);
 }
@@ -2739,6 +2764,10 @@
   if (Trap.hasErrorOccurred())
     return Sema::TDK_SubstitutionFailure;
 
+  if (TemplateDeductionResult Result
+        = CheckDeducedArgumentConstraints(*this, Partial, DeducedArgs, Info))
+    return Result;
+
   return ::FinishTemplateArgumentDeduction(
       *this, Partial, /*PartialOrdering=*/false, TemplateArgs, Deduced, Info);
 }
@@ -2848,6 +2877,12 @@
     = TemplateArgumentList::CreateCopy(Context, Builder);
   Info.reset(ExplicitArgumentList);
 
+  if (TemplateDeductionResult Result
+        = CheckDeducedArgumentConstraints(*this, FunctionTemplate,
+                                          ExplicitArgumentList->asArray(),
+                                          Info))
+    return Result;
+
   // Template argument deduction and the final substitution should be
   // done in the context of the templated declaration.  Explicit
   // argument substitution, on the other hand, needs to happen in the
@@ -3155,6 +3190,11 @@
           PartialOverloading))
     return Result;
 
+  if (TemplateDeductionResult Result
+        = CheckDeducedArgumentConstraints(*this, FunctionTemplate, DeducedArgs,
+                                          Info))
+    return Result;
+
   // C++ [temp.deduct.call]p10: [DR1391]
   //   If deduction succeeds for all parameters that contain
   //   template-parameters that participate in template argument deduction,
@@ -4911,6 +4951,7 @@
 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
Index: lib/Sema/SemaTemplate.cpp
===================================================================
--- lib/Sema/SemaTemplate.cpp
+++ lib/Sema/SemaTemplate.cpp
@@ -2906,8 +2906,7 @@
 
   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())
@@ -2926,11 +2925,15 @@
                                 false, Converted))
     return QualType();
 
+  if (EnsureTemplateArgumentListConstraints(Template, Converted, TemplateLoc))
+    return QualType();
+
   QualType CanonType;
 
   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())
@@ -3521,6 +3524,10 @@
                                 false, Converted))
     return true;
 
+  if (EnsureTemplateArgumentListConstraints(VarTemplate, Converted,
+                                            TemplateNameLoc))
+    return true;
+
   // Find the variable template (partial) specialization declaration that
   // corresponds to these arguments.
   if (IsPartialSpecialization) {
@@ -3699,6 +3706,10 @@
           Converted))
     return true;
 
+  if (EnsureTemplateArgumentListConstraints(Template, Converted,
+                                            TemplateNameLoc))
+    return true;
+
   // Find the variable template specialization declaration that
   // corresponds to these arguments.
   void *InsertPos = nullptr;
@@ -4796,13 +4807,243 @@
   return diagnoseArityMismatch(S, TD, Loc, Args);
 }
 
+template<typename TemplateDeclT>
+bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template,
+                                 const Expr *ConstraintExpr,
+                                 ArrayRef<TemplateArgument> TemplateArgs,
+                                 SourceLocation TemplateNameLoc,
+                                 bool &IsSatisfied,
+                                 ExprResult &SubstitutedExpression,
+                               llvm::Optional<PartialDiagnosticAt>& SubstDiag) {
+  if (!ConstraintExpr) {
+    IsSatisfied = true;
+    SubstitutedExpression.set(nullptr);
+    return false;
+  }
+
+  EnterExpressionEvaluationContext ConstantEvaluated(
+      S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+
+  MultiLevelTemplateArgumentList MLTAL;
+  MLTAL.addOuterTemplateArguments(TemplateArgs);
+
+  {
+    // We do not want error diagnostics escaping here.
+    TemplateDeductionInfo Info(TemplateNameLoc);
+    Sema::InstantiatingTemplate Inst(S, TemplateNameLoc, Template, TemplateArgs,
+                                     Info);
+    Sema::SFINAETrap Trap(S);
+
+    SubstitutedExpression = S.SubstExpr(const_cast<Expr*>(ConstraintExpr),
+                                        MLTAL);
+    if (SubstitutedExpression.isInvalid() || !SubstitutedExpression.isUsable()
+        || 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()) {
+        SubstDiag = PartialDiagnosticAt{ SourceLocation(),
+                                         PartialDiagnostic::NullDiagnostic() };
+        Info.takeSFINAEDiagnostic(SubstDiag.getValue());
+      }
+      IsSatisfied = false;
+      return false;
+    }
+  }
+
+  if (!S.CheckConstraintExpression(SubstitutedExpression.get())) {
+    return true;
+  }
+
+  if (SubstitutedExpression.get()->isInstantiationDependent()) {
+    IsSatisfied = true;
+    return false;
+  }
+
+  bool Satisfied = false;
+  if (!SubstitutedExpression.get()->EvaluateAsBooleanCondition(Satisfied,
+                                                               S.Context)) {
+      // C++2a [temp.constr.atomic]p1
+      //   ...E shall be a constant expression of type bool.
+    S.Diag(ConstraintExpr->getLocStart(),
+         diag::err_concept_non_constant_constraint_expression)
+        << SubstitutedExpression.get();
+    return true;
+  }
+  IsSatisfied = Satisfied;
+  return false;
+}
+
+bool Sema::CheckConstraintSatisfaction(TemplateDecl *Template,
+                                       const Expr *ConstraintExpr,
+                                       ArrayRef<TemplateArgument> TemplateArgs,
+                                       SourceLocation TemplateNameLoc,
+                                       bool &IsSatisfied,
+                                       ExprResult &SubstitutedExpression,
+                               llvm::Optional<PartialDiagnosticAt>& SubstDiag) {
+  return ::CheckConstraintSatisfaction(*this, Template, ConstraintExpr,
+                                       TemplateArgs, TemplateNameLoc,
+                                       IsSatisfied, SubstitutedExpression,
+                                       SubstDiag);
+}
+
+bool
+Sema::CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl* Part,
+                                  const Expr *ConstraintExpr,
+                                  ArrayRef<TemplateArgument> TemplateArgs,
+                                  SourceLocation TemplateNameLoc,
+                                  bool &IsSatisfied,
+                                  ExprResult &SubstitutedExpression,
+                               llvm::Optional<PartialDiagnosticAt>& SubstDiag) {
+  return ::CheckConstraintSatisfaction(*this, Part, ConstraintExpr,
+                                       TemplateArgs, TemplateNameLoc,
+                                       IsSatisfied, SubstitutedExpression,
+                                       SubstDiag);
+}
+
+bool
+Sema::CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl* Partial,
+                                  const Expr *ConstraintExpr,
+                                  ArrayRef<TemplateArgument> TemplateArgs,
+                                  SourceLocation TemplateNameLoc,
+                                  bool &IsSatisfied,
+                                  ExprResult &SubstitutedExpression,
+                               llvm::Optional<PartialDiagnosticAt>& SubstDiag) {
+  return ::CheckConstraintSatisfaction(*this, Partial, ConstraintExpr,
+                                       TemplateArgs, TemplateNameLoc,
+                                       IsSatisfied, SubstitutedExpression,
+                                       SubstDiag);
+}
+
+bool Sema::EnsureTemplateArgumentListConstraints(TemplateDecl *TD,
+                                     ArrayRef<TemplateArgument> TemplateArgs,
+                                               SourceLocation TemplateNameLoc) {
+  bool IsSatisfied;
+  ExprResult SubstitutedConstraintExpr;
+  llvm::Optional<PartialDiagnosticAt> SubstDiag;
+  if (CheckConstraintSatisfaction(TD, TD->getAssociatedConstraints(),
+                                  TemplateArgs, TemplateNameLoc, IsSatisfied,
+                                  SubstitutedConstraintExpr, SubstDiag))
+    return true;
+
+  if (!IsSatisfied) {
+    SmallString<128> TemplateArgString;
+    TemplateArgString = " ";
+    TemplateArgString += getTemplateArgumentBindingsText(
+        TD->getTemplateParameters(), TemplateArgs.data(), TemplateArgs.size());
+    Diag(TemplateNameLoc, diag::err_template_arg_list_constraints_not_satisfied)
+        << (int)getTemplateNameKindForDiagnostics(TemplateName(TD)) << TD
+        << TemplateArgString;
+    DiagnoseUnsatisfiedConstraint(SubstitutedConstraintExpr,
+                                  SubstDiag.getPointer());
+    return true;
+  }
+  return false;
+}
+
+static void diagnoseUnsatisfiedConstraintExpr(Sema &S, Expr *E,
+                                              bool First = true) {
+  if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) {
+    switch(BO->getOpcode()) {
+      case BO_LAnd: {
+        // Either LHS or RHS evaluated to false.
+        bool Result = false;
+        BO->getLHS()->EvaluateAsBooleanCondition(Result, S.Context);
+        if (Result) {
+          // LHS evaluated to true - therefore RHS evaluated to false.
+          diagnoseUnsatisfiedConstraintExpr(S, BO->getRHS(), First);
+          return;
+        }
+        // LHS evaluated to false - diagnose it.
+        diagnoseUnsatisfiedConstraintExpr(S, BO->getLHS(), First);
+        return;
+      }
+
+      case BO_LOr:
+        // Both LHS and RHS evaluated to false.
+        diagnoseUnsatisfiedConstraintExpr(S, BO->getLHS(), First);
+        diagnoseUnsatisfiedConstraintExpr(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()) {
+          llvm::APSInt SimplifiedLHS;
+          llvm::APSInt SimplifiedRHS;
+          BO->getLHS()->EvaluateAsInt(SimplifiedLHS, S.Context);
+          BO->getRHS()->EvaluateAsInt(SimplifiedRHS, S.Context);
+          S.Diag(E->getSourceRange().getBegin(),
+                 diag::note_atomic_constraint_evaluated_to_false_elaborated)
+              << (int) First << E << SimplifiedLHS.toString(10)
+              << BinaryOperator::getOpcodeStr(BO->getOpcode())
+              << SimplifiedRHS.toString(10);
+          return;
+        }
+        break;
+
+      default:
+        break;
+    }
+  } else if (ParenExpr *PE = dyn_cast<ParenExpr>(E)) {
+    diagnoseUnsatisfiedConstraintExpr(S, PE->getSubExpr(), First);
+    return;
+  } else if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(E)) {
+    if (CSE->getTemplateArgumentListInfo()->size() == 1) {
+      S.Diag(E->getSourceRange().getBegin(),
+          diag::note_single_arg_concept_specialization_constraint_evaluated_to_false)
+          << (int)First
+          << CSE->getTemplateArgumentListInfo()->arguments()[0].getArgument()
+          << CSE->getSpecializedConcept();
+    } else {
+      S.Diag(E->getSourceRange().getBegin(),
+          diag::note_concept_specialization_constraint_evaluated_to_false)
+          << (int)First << E;
+    }
+    diagnoseUnsatisfiedConstraintExpr(S, CSE->getSubstitutedConstraintExpr(),
+                                      /*First=*/true);
+    return;
+  }
+  S.Diag(E->getSourceRange().getBegin(),
+         diag::note_atomic_constraint_evaluated_to_false) << (int)First << E;
+}
+
+void Sema::DiagnoseUnsatisfiedConstraint(ExprResult UnsatisfiedConstraintExpr,
+                                         PartialDiagnosticAt *PDiag) {
+  if (UnsatisfiedConstraintExpr.isInvalid()) {
+    SmallString<128> SFINAEArgString;
+    SourceRange R;
+    if (PDiag) {
+      SFINAEArgString = ": ";
+      R = SourceRange(PDiag->first, PDiag->first);
+      PDiag->second.EmitToString(getDiagnostics(), SFINAEArgString);
+    }
+    if (UnsatisfiedConstraintExpr.get())
+      Diag(UnsatisfiedConstraintExpr.get()->getLocStart(),
+           diag::note_substituted_constraint_expr_is_ill_formed)
+        << UnsatisfiedConstraintExpr.get() << SFINAEArgString << R;
+    else
+      Diag(R.getBegin(),
+           diag::note_substituted_constraint_expr_is_ill_formed_no_expr)
+        << SFINAEArgString << R;
+
+    return;
+  }
+  diagnoseUnsatisfiedConstraintExpr(*this, UnsatisfiedConstraintExpr.get());
+}
+
 /// \brief Check that the given template argument list is well-formed
 /// for specializing the given template.
 bool Sema::CheckTemplateArgumentList(
     TemplateDecl *Template, SourceLocation TemplateLoc,
     TemplateArgumentListInfo &TemplateArgs, bool PartialTemplateArgs,
     SmallVectorImpl<TemplateArgument> &Converted,
     bool UpdateArgsWithConversions) {
+
   // 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.
@@ -7416,6 +7657,10 @@
                                 TemplateArgs, false, Converted))
     return true;
 
+  if (EnsureTemplateArgumentListConstraints(ClassTemplate, Converted,
+                                            TemplateNameLoc))
+    return true;
+
   // Find the class template (partial) specialization declaration that
   // corresponds to these arguments.
   if (isPartialSpecialization) {
@@ -8592,6 +8837,11 @@
                                 TemplateArgs, false, Converted))
     return true;
 
+  if (EnsureTemplateArgumentListConstraints(ClassTemplate, Converted,
+                                            TemplateNameLoc)) {
+    return true;
+  }
+
   // Find the class template specialization declaration that
   // corresponds to these arguments.
   void *InsertPos = nullptr;
Index: lib/Sema/SemaOverload.cpp
===================================================================
--- lib/Sema/SemaOverload.cpp
+++ lib/Sema/SemaOverload.cpp
@@ -578,6 +578,10 @@
     TemplateArgumentList *TemplateArgs;
     unsigned CallArgIndex;
   };
+  struct CNSInfo {
+    TemplateArgumentList *TemplateArgs;
+    ExprResult SubstitutedConstraintExpr;
+  };
 }
 
 /// \brief Convert from Sema's representation of template deduction information
@@ -646,6 +650,21 @@
     }
     break;
 
+  case Sema::TDK_ConstraintsNotSatisfied: {
+    CNSInfo *Saved = new (Context) CNSInfo;
+    Saved->TemplateArgs = Info.take();
+    Saved->SubstitutedConstraintExpr = Info.SubstitutedConstraintExpr;
+    Result.Data = Saved;
+
+    if (Info.hasSFINAEDiagnostic()) {
+      PartialDiagnosticAt *Diag = new (Result.Diagnostic) PartialDiagnosticAt(
+          SourceLocation(), PartialDiagnostic::NullDiagnostic());
+      Info.takeSFINAEDiagnostic(*Diag);
+      Result.HasDiagnostic = true;
+    }
+    break;
+  }
+
   case Sema::TDK_Success:
   case Sema::TDK_NonDependentConversionFailure:
     llvm_unreachable("not a deduction failure");
@@ -685,6 +704,15 @@
     }
     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;
@@ -710,6 +738,7 @@
   case Sema::TDK_NonDeducedMismatch:
   case Sema::TDK_CUDATargetMismatch:
   case Sema::TDK_NonDependentConversionFailure:
+  case Sema::TDK_ConstraintsNotSatisfied:
     return TemplateParameter();
 
   case Sema::TDK_Incomplete:
@@ -751,6 +780,9 @@
   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;
@@ -771,6 +803,7 @@
   case Sema::TDK_SubstitutionFailure:
   case Sema::TDK_CUDATargetMismatch:
   case Sema::TDK_NonDependentConversionFailure:
+  case Sema::TDK_ConstraintsNotSatisfied:
     return nullptr;
 
   case Sema::TDK_Inconsistent:
@@ -800,6 +833,7 @@
   case Sema::TDK_SubstitutionFailure:
   case Sema::TDK_CUDATargetMismatch:
   case Sema::TDK_NonDependentConversionFailure:
+  case Sema::TDK_ConstraintsNotSatisfied:
     return nullptr;
 
   case Sema::TDK_Inconsistent:
@@ -9865,6 +9899,22 @@
     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)->SubstitutedConstraintExpr,
+       DeductionFailure.getSFINAEDiagnostic());
+    return;
+  }
   case Sema::TDK_TooManyArguments:
   case Sema::TDK_TooFewArguments:
     DiagnoseArityMismatch(S, Found, Templated, NumArgs);
@@ -10271,15 +10321,18 @@
   case Sema::TDK_CUDATargetMismatch:
     return 3;
 
-  case Sema::TDK_InstantiationDepth:
+  case Sema::TDK_ConstraintsNotSatisfied:
     return 4;
 
-  case Sema::TDK_InvalidExplicitArguments:
+  case Sema::TDK_InstantiationDepth:
     return 5;
 
+  case Sema::TDK_InvalidExplicitArguments:
+    return 6;
+
   case Sema::TDK_TooManyArguments:
   case Sema::TDK_TooFewArguments:
-    return 6;
+    return 7;
   }
   llvm_unreachable("Unhandled deduction result");
 }
Index: lib/AST/ExprCXX.cpp
===================================================================
--- lib/AST/ExprCXX.cpp
+++ lib/AST/ExprCXX.cpp
@@ -1453,56 +1453,6 @@
 ConceptSpecializationExpr::ConceptSpecializationExpr(ASTContext &C)
   : Expr(ConceptSpecializationExprClass, EmptyShell()) { }
 
-bool ConceptSpecializationExpr::evaluate(Sema &S)
-{
-  llvm::SmallVector<TemplateArgument, 4> Converted;
-  if (S.CheckTemplateArgumentList(SpecializedConcept,
-                                  SpecializedConcept->getLocStart(),
-                                  TemplateArgInfo,
-                                  /*PartialTemplateArgs=*/false, Converted,
-                                  /*UpdateArgsWithConversion=*/false)) {
-    // We converted these arguments back in CheckConceptTemplateId, this should
-    // work.
-    return true;
-  }
-  EnterExpressionEvaluationContext ConstantEvaluated(
-          S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
-
-
-  MultiLevelTemplateArgumentList MLTAL;
-  MLTAL.addOuterTemplateArguments(Converted);
-
-  ExprResult E;
-  {
-    // We do not want error diagnostics escaping here.
-    Sema::SFINAETrap Trap(S);
-    E = S.SubstExpr(SpecializedConcept->getConstraintExpr(), 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 true;
-    }
-  }
-
-  if (!S.CheckConstraintExpression(E.get())) {
-    S.Diag(getLocStart(), diag::note_in_concept_specialization) << this;
-    return true;
-  }
-
-  bool Satisfied = false;
-  if (!E.get()->EvaluateAsBooleanCondition(Satisfied, S.Context)) {
-      // C++2a [temp.constr.atomic]p1
-      //   ...E shall be a constant expression of type bool.
-    S.Diag(getLocStart(), diag::err_concept_non_constant_constraint_expression)
-      << this << E.get();
-    return true;
-  }
-  IsSatisfied = Satisfied;
-  return false;
-}
-
 bool ConceptSpecializationExpr::setTemplateArguments(Sema *S,
                                           const TemplateArgumentListInfo *TALI){
   bool IsDependent = false;
@@ -1526,7 +1476,25 @@
   setInstantiationDependent(IsDependent);
   setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack);
   if (!IsDependent && S) {
-    return evaluate(*S);
+    llvm::SmallVector<TemplateArgument, 4> Converted;
+    if (S->CheckTemplateArgumentList(SpecializedConcept,
+                                     SpecializedConcept->getLocStart(),
+                                     TemplateArgInfo,
+                                     /*PartialTemplateArgs=*/false, Converted,
+                                     /*UpdateArgsWithConversion=*/false)) {
+      // We converted these arguments back in CheckConceptTemplateId, this should
+      // work.
+      return true;
+    }
+    ExprResult SubstitutedExpression;
+    if (S->CheckConstraintSatisfaction(SpecializedConcept,
+                                       SpecializedConcept->getConstraintExpr(),
+                                       Converted, ConceptNameLoc, IsSatisfied,
+                                       SubstitutedExpression, SubstDiagnostic)){
+      S->Diag(getLocStart(), diag::note_in_concept_specialization) << this;
+      return true;
+    }
+    SubstitutedConstraintExpr = SubstitutedExpression.get();
   }
   return false;
 }
\ No newline at end of file
Index: include/clang/Sema/TemplateDeduction.h
===================================================================
--- include/clang/Sema/TemplateDeduction.h
+++ include/clang/Sema/TemplateDeduction.h
@@ -13,6 +13,7 @@
 #ifndef LLVM_CLANG_SEMA_TEMPLATEDEDUCTION_H
 #define LLVM_CLANG_SEMA_TEMPLATEDEDUCTION_H
 
+#include "clang/Sema/Ownership.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/Basic/PartialDiagnostic.h"
 #include "llvm/ADT/SmallVector.h"
@@ -192,6 +193,10 @@
   ///
   /// FIXME: This should be kept internal to SemaTemplateDeduction.
   SmallVector<DeducedPack *, 8> PendingDeducedPacks;
+
+  /// \brief The substituted constraint expression used to determine whether the
+  /// associated constraints were satisfied.
+  ExprResult SubstitutedConstraintExpr;
 };
 
 } // end namespace sema
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -5576,15 +5576,73 @@
                                              ConceptDecl *CTD,
                                        const TemplateArgumentListInfo *TALI);
 
-  /// Check whether the given expression is a valid constraint expression.
-  /// A diagnostic is emmited if it is not, and false is returned.
+  /// \brief Check whether the given expression is a valid constraint
+  /// expression. A diagnostic is emmited if it is not, and false is returned.
   bool CheckConstraintExpression(Expr *CE);
 
+  /// \brief Check whether the given constraint expression is satisfied given
+  /// template arguments. Returns false and updates IsSatisfied with the
+  /// satisfaction verdict if successful, emits a diagnostic and returns true if
+  /// an error occured and satisfaction could not be determined.
+  ///
+  /// \param SubstitutedExpr if not null, will be used to return the substituted
+  /// constraint expression (to be used for diagnostics, for example).
+  ///
+  /// \param SubstDiag if the constraint was not satisfied because of a
+  /// substitution failure, this will contain the emitted diagnostics, if any.
+  ///
+  /// \returns true if an error occurred, false otherwise.
+  bool CheckConstraintSatisfaction(TemplateDecl *Template,
+                                   const Expr *ConstraintExpr,
+                                   ArrayRef<TemplateArgument> TemplateArgs,
+                                   SourceLocation TemplateNameLoc,
+                                   bool &IsSatisfied,
+                                   ExprResult &SubstitutedExpression,
+                                llvm::Optional<PartialDiagnosticAt>& SubstDiag);
+
+  bool CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl *TD,
+                                   const Expr *ConstraintExpr,
+                                   ArrayRef<TemplateArgument> TemplateArgs,
+                                   SourceLocation TemplateNameLoc,
+                                   bool &IsSatisfied,
+                                   ExprResult &SubstitutedExpression,
+                                llvm::Optional<PartialDiagnosticAt>& SubstDiag);
+
+  bool CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl *TD,
+                                   const Expr *ConstraintExpr,
+                                   ArrayRef<TemplateArgument> TemplateArgs,
+                                   SourceLocation TemplateNameLoc,
+                                   bool &IsSatisfied,
+                                   ExprResult &SubstitutedExpression,
+                                llvm::Optional<PartialDiagnosticAt>& SubstDiag);
+
   /// \brief Check that the associated constraints of a template declaration
   /// match the associated constraints of an older declaration of which it is a
   /// redeclaration
   bool CheckRedeclarationConstraintMatch(const Expr *OldAC, const Expr *NewAC);
 
+  /// \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 TemplateNameLoc Where was the template name that evoked this
+  /// 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,
+                                             SourceLocation TemplateNameLoc);
+
+  /// \brief Emit diagnostics explaining why a constraint expression was deemed
+  /// unsatisfied.
+  void DiagnoseUnsatisfiedConstraint(ExprResult SubstitutedConstraintExpr,
+                                     PartialDiagnosticAt *PDiag);
+
   // ParseObjCStringLiteral - Parse Objective-C string literals.
   ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs,
                                     ArrayRef<Expr *> Strings);
@@ -6899,6 +6957,9 @@
     TDK_InvalidExplicitArguments,
     /// \brief Checking non-dependent argument conversions failed.
     TDK_NonDependentConversionFailure,
+    /// \brief The deduced arguments did not satisfy the constraints associated
+    /// with the template.
+    TDK_ConstraintsNotSatisfied,
     /// \brief Deduction failed; that's all we know.
     TDK_MiscellaneousDeductionFailure,
     /// \brief CUDA Target attributes do not match.
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -2402,11 +2402,28 @@
 def err_concept_no_associated_constraints : Error<
   "concept may not have associated constraints">;
 def err_concept_non_constant_constraint_expression : Error<
-  "concept specialization '%0' resulted in a non-constant expression '%1'.">;
+  "substitution into constraint expression resulted in a non-constant expression '%0'">;
 def err_non_bool_atomic_constraint : Error<
   "atomic constraint '%0' must be of type 'bool' (found %1)">;
 def note_in_concept_specialization : Note<
   "in concept specialization '%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, because:">;
+def note_constraints_not_satisfied : Note<
+  "constraints not satisfied">;
+def note_substituted_constraint_expr_is_ill_formed : Note<
+  "substituted constraint expression '%0' is ill-formed%1">;
+def note_substituted_constraint_expr_is_ill_formed_no_expr : Note<
+  "substituted constraint expression is ill-formed%0">;
+def note_atomic_constraint_evaluated_to_false : Note<
+  "%select{and |}0'%1' evaluated to false">;
+def note_concept_specialization_constraint_evaluated_to_false : Note<
+  "%select{and |}0'%1' evaluated to false, because:">;
+def note_single_arg_concept_specialization_constraint_evaluated_to_false : Note<
+  "%select{and |}0%1 does not satisfy %2, because:">;
+def note_atomic_constraint_evaluated_to_false_elaborated : Note<
+  "%select{and |}0'%1' (%2 %3 %4) evaluated to false">;
 
 def err_template_different_associated_constraints : Error<
   "associated constraints differ in template redeclaration">;
@@ -3540,6 +3557,8 @@
 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">;
Index: include/clang/AST/ExprCXX.h
===================================================================
--- include/clang/AST/ExprCXX.h
+++ include/clang/AST/ExprCXX.h
@@ -4430,12 +4430,19 @@
   /// when the expression was created. If any of the template arguments are
   /// dependent (this expr would then be isValueDependent()), this is to be
   /// ignored.
-  bool IsSatisfied : 1;
+  bool IsSatisfied;
 
-  /// \brief Evaluates this concept specialization to determine whether or not
-  /// the concept is satisfied. Returns true if an error occured and the concept
-  /// could not be checked for satisfaction.
-  bool evaluate(Sema &S);
+  /// \brief The concept's constraint expression with the template arguments
+  /// substituted in. To be used in diagnostics. If any of the template
+  /// arguments are dependent (this expr would then be isValueDependent()), this
+  /// is to be ignored.
+  Expr *SubstitutedConstraintExpr;
+
+  /// \brief If the concept's constraint expression was not satisfied because of
+  /// an ill-formed substitution, this contains diagnostics emitted when
+  /// substituting the template arguments into the constraint expression, to be
+  /// used in elaborated diagnostics.
+  llvm::Optional<PartialDiagnosticAt> SubstDiagnostic;
 
 public:
   ConceptSpecializationExpr(ASTContext &C, Sema &S,
@@ -4479,6 +4486,14 @@
     IsSatisfied = Satisfied;
   }
 
+  Expr *getSubstitutedConstraintExpr() const {
+    return SubstitutedConstraintExpr;
+  }
+
+  void setSubstitutedConstraintExpr(Expr *SCE) {
+    SubstitutedConstraintExpr = SCE;
+  }
+
   SourceLocation getConceptNameLoc() const { return ConceptNameLoc; }
   void setConceptNameLoc(SourceLocation Loc) {
     ConceptNameLoc = Loc;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to