usaxena95 updated this revision to Diff 475054.
usaxena95 marked 3 inline comments as done.
usaxena95 added a comment.

Addressed commented. Added Release notes.
Removed infrastructure changes and deferred them to a future patch.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D137712/new/

https://reviews.llvm.org/D137712

Files:
  clang/docs/ReleaseNotes.rst
  clang/include/clang/AST/ExprConcepts.h
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/AST/ComputeDependence.cpp
  clang/lib/AST/ExprConcepts.cpp
  clang/lib/AST/StmtPrinter.cpp
  clang/lib/Sema/SemaConcept.cpp
  clang/lib/Sema/TreeTransform.h
  clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp

Index: clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
===================================================================
--- clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
+++ clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
@@ -51,3 +51,80 @@
   X.next();
 };
 
+
+namespace SubstitutionFailureNestedRequires {
+template<class T>  concept True = true;
+
+struct S { double value; };
+
+template <class T>
+concept Pipes = requires (T x) {
+   requires True<decltype(x.value)> || True<T>;
+   requires True<T> || True<decltype(x.value)>;
+};
+
+template <class T>
+concept Amps1 = requires (T x) {
+   requires True<decltype(x.value)> && True<T>;
+   // expected-note@-1{{because 'member reference base type 'int' is not a structure or union'}}
+};
+template <class T>
+concept Amps2 = requires (T x) {
+   requires True<T> && True<decltype(x.value)>;
+};
+
+static_assert(Pipes<S>);
+static_assert(Pipes<double>);
+
+static_assert(Amps1<S>);
+static_assert(!Amps1<double>);
+
+static_assert(Amps2<S>);
+static_assert(!Amps2<double>);
+
+template<class T>
+void foo1() requires requires (T x) { // expected-note {{candidate template ignored: constraints not satisfied [with T = int]}}
+  requires
+  True<decltype(x.value)> // expected-note {{because 'member reference base type 'int' is not a structure or union'}}
+  && True<T>;
+} {}
+template<class T> void fooPipes() requires Pipes<T> {}
+template<class T> void fooAmps1() requires Amps1<T> {}
+// expected-note@-1 {{candidate template ignored: constraints not satisfied [with T = int]}} \
+// expected-note@-1 {{because 'int' does not satisfy 'Amps1'}}
+
+void foo() {
+  foo1<S>();
+  foo1<int>(); // expected-error {{no matching function for call to 'foo1'}}
+  fooPipes<S>();
+  fooPipes<int>();
+  fooAmps1<S>();
+  fooAmps1<int>(); // expected-error {{no matching function for call to 'fooAmps1'}}
+}
+
+template<class T>
+concept HasNoValue = requires (T x) {
+  requires !True<decltype(x.value)> && True<T>;
+};
+static_assert(HasNoValue<int>);
+static_assert(!HasNoValue<S>);
+
+template<class T> constexpr bool NotAConceptTrue = true;
+template <class T>
+concept SFinNestedRequires = requires (T x) {
+    // Only SF in a concept specialisation should be evaluated to false. 
+    // Rest is still a SF.
+   requires NotAConceptTrue<decltype(x.value)> || NotAConceptTrue<T>;
+   // expected-note@-1 {{because 'NotAConceptTrue<decltype(x.value)> || NotAConceptTrue<T>' would be invalid: member reference base type 'int' is not a structure or union}}
+};
+static_assert(!SFinNestedRequires<int>); // Unsatisfied but not a hard error.
+static_assert(SFinNestedRequires<S>);
+template <class T>
+void foo() requires SFinNestedRequires<T> {} 
+// expected-note@-1 {{candidate template ignored: constraints not satisfied [with T = int]}}
+// expected-note@-2 {{because 'int' does not satisfy 'SFinNestedRequires'}}
+void bar() { 
+  foo<int>(); // expected-error {{no matching function for call to 'foo'}}
+  foo<S>();
+}
+}
Index: clang/lib/Sema/TreeTransform.h
===================================================================
--- clang/lib/Sema/TreeTransform.h
+++ clang/lib/Sema/TreeTransform.h
@@ -15,6 +15,7 @@
 
 #include "CoroutineStmtBuilder.h"
 #include "TypeLocBuilder.h"
+#include "clang/AST/ASTConcept.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
@@ -37,6 +38,7 @@
 #include "clang/Sema/ScopeInfo.h"
 #include "clang/Sema/SemaDiagnostic.h"
 #include "clang/Sema/SemaInternal.h"
+#include "clang/Sema/TemplateDeduction.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/Support/ErrorHandling.h"
 #include <algorithm>
@@ -3477,6 +3479,23 @@
     return Result;
   }
 
+  ExprResult RebuildConceptSpecializationSubstitutionFailure(
+      ConceptSpecializationExpr *E,
+      ConstraintSatisfaction::SubstitutionDiagnostic *SubstDiags) {
+    ConstraintSatisfaction Satisfaction;
+    Satisfaction.IsSatisfied = false;
+    Satisfaction.ContainsErrors = false;
+    Satisfaction.Details.emplace_back(E, SubstDiags);
+    CXXScopeSpec SS;
+    SS.Adopt(E->getNestedNameSpecifierLoc());
+    return ConceptSpecializationExpr::CreateSubstitutionFailure(
+        SemaRef.Context,
+        SS.isSet() ? SS.getWithLocInContext(SemaRef.Context)
+                   : NestedNameSpecifierLoc{},
+        E->getTemplateKWLoc(), E->getConceptNameInfo(), E->getFoundDecl(),
+        E->getNamedConcept(), &Satisfaction);
+  }
+
   /// \brief Build a new requires expression.
   ///
   /// By default, performs semantic analysis to build the new expression.
@@ -12611,15 +12630,42 @@
                                        E->getEndLoc());
 }
 
-template<typename Derived>
-ExprResult
-TreeTransform<Derived>::TransformConceptSpecializationExpr(
-                                                 ConceptSpecializationExpr *E) {
+namespace {
+// TODO: Use SubstitutionDiagnostic instead of pair<Loc, Message> to propagate
+// SubstitutedEntity.
+// TODO: Refactor to use CreateSubstDiag from SemaTemplateInstantiate.cpp.
+inline ConstraintSatisfaction::SubstitutionDiagnostic *
+CreateSubstDiag(Sema &S, sema::TemplateDeductionInfo &Info) {
+  SmallString<128> Message;
+  SourceLocation ErrorLoc;
+
+  PartialDiagnosticAt PDA(SourceLocation(),
+                          PartialDiagnostic::NullDiagnostic{});
+  Info.takeSFINAEDiagnostic(PDA);
+  PDA.second.EmitToString(S.getDiagnostics(), Message);
+  ErrorLoc = PDA.first;
+  char *MessageBuf = new (S.Context) char[Message.size()];
+  std::copy(Message.begin(), Message.end(), MessageBuf);
+  return new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{
+      ErrorLoc, StringRef(MessageBuf, Message.size())};
+}
+} // namespace
+
+template <typename Derived>
+ExprResult TreeTransform<Derived>::TransformConceptSpecializationExpr(
+    ConceptSpecializationExpr *E) {
   const ASTTemplateArgumentListInfo *Old = E->getTemplateArgsAsWritten();
   TemplateArgumentListInfo TransArgs(Old->LAngleLoc, Old->RAngleLoc);
-  if (getDerived().TransformTemplateArguments(Old->getTemplateArgs(),
-                                              Old->NumTemplateArgs, TransArgs))
-    return ExprError();
+  {
+    Sema::SFINAETrap Trap(getSema());
+    if (getDerived().TransformTemplateArguments(
+            Old->getTemplateArgs(), Old->NumTemplateArgs, TransArgs)) {
+      if (!E->isInstantiationDependent() || !Trap.hasErrorOccurred())
+        return ExprError();
+      return getDerived().RebuildConceptSpecializationSubstitutionFailure(
+          E, CreateSubstDiag(SemaRef, **SemaRef.isSFINAEContext()));
+    }
+  }
 
   return getDerived().RebuildConceptSpecializationExpr(
       E->getNestedNameSpecifierLoc(), E->getTemplateKWLoc(),
Index: clang/lib/Sema/SemaConcept.cpp
===================================================================
--- clang/lib/Sema/SemaConcept.cpp
+++ clang/lib/Sema/SemaConcept.cpp
@@ -11,6 +11,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "TreeTransform.h"
+#include "clang/AST/ASTConcept.h"
 #include "clang/Sema/SemaConcept.h"
 #include "clang/Sema/Sema.h"
 #include "clang/Sema/SemaInternal.h"
@@ -941,6 +942,17 @@
       break;
     }
   } else if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(SubstExpr)) {
+    if (CSE->hasSubstitutionFailureInArgs()) {
+      for (const auto &Diags : CSE->getSatisfaction()) {
+        auto *SubstDiag =
+            Diags.second
+                .get<ConstraintSatisfaction::SubstitutionDiagnostic *>();
+        S.Diag(SubstDiag->first,
+               diag::note_concept_specialisation_substitution_error)
+            << (int)First << SubstDiag->second;
+      }
+      return;
+    }
     if (CSE->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
       S.Diag(
           CSE->getSourceRange().getBegin(),
Index: clang/lib/AST/StmtPrinter.cpp
===================================================================
--- clang/lib/AST/StmtPrinter.cpp
+++ clang/lib/AST/StmtPrinter.cpp
@@ -2467,9 +2467,12 @@
   if (E->getTemplateKWLoc().isValid())
     OS << "template ";
   OS << E->getFoundDecl()->getName();
-  printTemplateArgumentList(OS, E->getTemplateArgsAsWritten()->arguments(),
-                            Policy,
-                            E->getNamedConcept()->getTemplateParameters());
+  if (E->hasSubstitutionFailureInArgs())
+    OS << "<<error-expression>>";
+  else
+    printTemplateArgumentList(OS, E->getTemplateArgsAsWritten()->arguments(),
+                              Policy,
+                              E->getNamedConcept()->getTemplateParameters());
 }
 
 void StmtPrinter::VisitRequiresExpr(RequiresExpr *E) {
Index: clang/lib/AST/ExprConcepts.cpp
===================================================================
--- clang/lib/AST/ExprConcepts.cpp
+++ clang/lib/AST/ExprConcepts.cpp
@@ -36,14 +36,15 @@
     NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
     const ASTTemplateArgumentListInfo *ArgsAsWritten,
     ImplicitConceptSpecializationDecl *SpecDecl,
-    const ConstraintSatisfaction *Satisfaction)
+    const ConstraintSatisfaction *Satisfaction, bool ArgsHasSubstitutionFailure)
     : Expr(ConceptSpecializationExprClass, C.BoolTy, VK_PRValue, OK_Ordinary),
       ConceptReference(NNS, TemplateKWLoc, ConceptNameInfo, FoundDecl,
                        NamedConcept, ArgsAsWritten),
       SpecDecl(SpecDecl),
       Satisfaction(Satisfaction
                        ? ASTConstraintSatisfaction::Create(C, *Satisfaction)
-                       : nullptr) {
+                       : nullptr),
+      ArgsHasSubstitutionFailure(ArgsHasSubstitutionFailure) {
   setDependence(computeDependence(this, /*ValueDependent=*/!Satisfaction));
 
   // Currently guaranteed by the fact concepts can only be at namespace-scope.
@@ -53,6 +54,13 @@
               ->containsUnexpandedParameterPack()));
   assert((!isValueDependent() || isInstantiationDependent()) &&
          "should not be value-dependent");
+  if (ArgsHasSubstitutionFailure) {
+    assert(Satisfaction);
+    assert(!Satisfaction->IsSatisfied);
+    assert(!Satisfaction->ContainsErrors);
+    assert(!ArgsAsWritten);
+    assert(!SpecDecl);
+  }
 }
 
 ConceptSpecializationExpr::ConceptSpecializationExpr(EmptyShell Empty)
@@ -103,6 +111,17 @@
                                 Dependent, ContainsUnexpandedParameterPack);
 }
 
+ConceptSpecializationExpr *ConceptSpecializationExpr::CreateSubstitutionFailure(
+    const ASTContext &C, NestedNameSpecifierLoc NNS,
+    SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo,
+    NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
+    const ConstraintSatisfaction *Satisfaction) {
+  return new (C) ConceptSpecializationExpr(
+      C, NNS, TemplateKWLoc, ConceptNameInfo, FoundDecl, NamedConcept,
+      /*ArgsAsWritten=*/nullptr, /*SpecDecl=*/nullptr, Satisfaction,
+      /*ArgsHasSubstitutionFailure=*/true);
+}
+
 const TypeConstraint *
 concepts::ExprRequirement::ReturnTypeRequirement::getTypeConstraint() const {
   assert(isTypeConstraint());
Index: clang/lib/AST/ComputeDependence.cpp
===================================================================
--- clang/lib/AST/ComputeDependence.cpp
+++ clang/lib/AST/ComputeDependence.cpp
@@ -844,12 +844,13 @@
   auto TA = TemplateArgumentDependence::None;
   const auto InterestingDeps = TemplateArgumentDependence::Instantiation |
                                TemplateArgumentDependence::UnexpandedPack;
-  for (const TemplateArgumentLoc &ArgLoc :
-       E->getTemplateArgsAsWritten()->arguments()) {
-    TA |= ArgLoc.getArgument().getDependence() & InterestingDeps;
-    if (TA == InterestingDeps)
-      break;
-  }
+  if (E->getTemplateArgsAsWritten())
+    for (const TemplateArgumentLoc &ArgLoc :
+         E->getTemplateArgsAsWritten()->arguments()) {
+      TA |= ArgLoc.getArgument().getDependence() & InterestingDeps;
+      if (TA == InterestingDeps)
+        break;
+    }
 
   ExprDependence D =
       ValueDependent ? ExprDependence::Value : ExprDependence::None;
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2889,6 +2889,8 @@
   "%select{and|because}0 '%1' would be invalid">;
 def note_nested_requirement_substitution_error : Note<
   "%select{and|because}0 '%1' would be invalid: %2">;
+def note_concept_specialisation_substitution_error : Note<
+  "%select{and|because}0 '%1'">;
 def note_nested_requirement_unknown_substitution_error : Note<
   "%select{and|because}0 '%1' would be invalid">;
 def note_ambiguous_atomic_constraints : Note<
Index: clang/include/clang/AST/ExprConcepts.h
===================================================================
--- clang/include/clang/AST/ExprConcepts.h
+++ clang/include/clang/AST/ExprConcepts.h
@@ -53,6 +53,7 @@
   /// given arguments. If this expression is value dependent, this is to be
   /// ignored.
   ASTConstraintSatisfaction *Satisfaction;
+  bool ArgsHasSubstitutionFailure = false;
 
   ConceptSpecializationExpr(const ASTContext &C, NestedNameSpecifierLoc NNS,
                             SourceLocation TemplateKWLoc,
@@ -60,7 +61,8 @@
                             NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
                             const ASTTemplateArgumentListInfo *ArgsAsWritten,
                             ImplicitConceptSpecializationDecl *SpecDecl,
-                            const ConstraintSatisfaction *Satisfaction);
+                            const ConstraintSatisfaction *Satisfaction,
+                            bool ArgsHasSubstitutionFailure = false);
 
   ConceptSpecializationExpr(const ASTContext &C, ConceptDecl *NamedConcept,
                             ImplicitConceptSpecializationDecl *SpecDecl,
@@ -84,10 +86,22 @@
          const ConstraintSatisfaction *Satisfaction, bool Dependent,
          bool ContainsUnexpandedParameterPack);
 
+  static ConceptSpecializationExpr *
+  CreateSubstitutionFailure(const ASTContext &C, NestedNameSpecifierLoc NNS,
+                            SourceLocation TemplateKWLoc,
+                            DeclarationNameInfo ConceptNameInfo,
+                            NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
+                            const ConstraintSatisfaction *Satisfaction);
+
   ArrayRef<TemplateArgument> getTemplateArguments() const {
+    assert(SpecDecl);
     return SpecDecl->getTemplateArguments();
   }
 
+  bool hasSubstitutionFailureInArgs() const {
+    return ArgsHasSubstitutionFailure;
+  }
+
   const ImplicitConceptSpecializationDecl *getSpecializationDecl() const {
     assert(SpecDecl && "Template Argument Decl not initialized");
     return SpecDecl;
@@ -122,6 +136,7 @@
   }
 
   SourceLocation getEndLoc() const LLVM_READONLY {
+    assert(ArgsAsWritten);
     // If the ConceptSpecializationExpr is the ImmediatelyDeclaredConstraint
     // of a TypeConstraint written syntactically as a constrained-parameter,
     // there may not be a template argument list.
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -586,9 +586,11 @@
   ([temp.func.order]p6.2.1 is not implemented, matching GCC).
 - Implemented `P0857R0 <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0857r0.html>`_,
   which specifies constrained lambdas and constrained template *template-parameter*\s.
-
 - Do not hide templated base members introduced via using-decl in derived class
   (useful specially for constrained members). Fixes `GH50886 <https://github.com/llvm/llvm-project/issues/50886>`_.
+- Substitution failure in concept specialisation now makes the concept unsatisfied instead of 
+  an invalid expression. Fixes evaluation of nested requirements involving such concepts 
+  along with logical binary operators: `Issue 45563 <https://github.com/llvm/llvm-project/issues/45563>`_.
 
 C++2b Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to