https://github.com/zyn0217 updated 
https://github.com/llvm/llvm-project/pull/218618

>From f09b887844acafd36f4cf66a46bc8d3b09b5b6c6 Mon Sep 17 00:00:00 2001
From: Younan Zhang <[email protected]>
Date: Tue, 25 Aug 2026 15:47:44 +0800
Subject: [PATCH 1/4] [Clang] Improve concept diagnostics

We used to drop SFINAE errors that occurred during parameter mapping 
instantiation
on the floor, making our diagnostics worse for some cases.

This patch corrects that behavior. Moreover it fixes some clients where
the errors were not properly handled for invalid expressions.
---
 clang/lib/Sema/SemaConcept.cpp                | 80 ++++++++-----------
 clang/lib/Sema/SemaTemplateDeduction.cpp      | 11 +--
 .../non-function-templates.cpp                |  2 +
 clang/test/SemaCXX/cxx23-assume.cpp           |  1 +
 clang/test/SemaCXX/cxx2c-fold-exprs.cpp       | 63 +++++++++++----
 clang/test/SemaTemplate/concepts.cpp          |  4 +
 .../SemaTemplate/cxx2a-constraint-exprs.cpp   |  1 +
 .../instantiate-requires-expr.cpp             |  1 +
 8 files changed, 94 insertions(+), 69 deletions(-)

diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index e6ef73b5a2d53..c6683180778be 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -555,6 +555,30 @@ class ConstraintSatisfactionChecker {
                                         : PackSubstitutionIndex;
   }
 
+  StringRef allocateStringFromConceptDiagnostic(const PartialDiagnostic &Diag) 
{
+    SmallString<128> DiagString;
+    DiagString = ": ";
+    Diag.EmitToString(S.getDiagnostics(), DiagString);
+    return S.getASTContext().backupStr(DiagString);
+  }
+
+  void consumeSFINAEFailure(TemplateDeductionInfo &Info,
+                            ConstraintSatisfaction &Satisfaction) {
+    PartialDiagnosticAt SubstDiag{SourceLocation(),
+                                  PartialDiagnostic::NullDiagnostic()};
+    Info.takeSFINAEDiagnostic(SubstDiag);
+    // FIXME: This is an unfortunate consequence of there
+    //  being no serialization code for PartialDiagnostics and the fact
+    //  that serializing them would likely take a lot more storage than
+    //  just storing them as strings. We would still like, in the
+    //  future, to serialize the proper PartialDiagnostic as serializing
+    //  it as a string defeats the purpose of the diagnostic mechanism.
+    Satisfaction.Details.emplace_back(
+        new (S.Context) ConstraintSubstitutionDiagnostic{
+            SubstDiag.first,
+            allocateStringFromConceptDiagnostic(SubstDiag.second)});
+  }
+
   ExprResult
   EvaluateAtomicConstraint(const Expr *AtomicExpr,
                            const MultiLevelTemplateArgumentList &MLTAL);
@@ -607,14 +631,6 @@ class ConstraintSatisfactionChecker {
                       const MultiLevelTemplateArgumentList &MLTAL);
 };
 
-StringRef allocateStringFromConceptDiagnostic(const Sema &S,
-                                              const PartialDiagnostic Diag) {
-  SmallString<128> DiagString;
-  DiagString = ": ";
-  Diag.EmitToString(S.getDiagnostics(), DiagString);
-  return S.getASTContext().backupStr(DiagString);
-}
-
 } // namespace
 
 ExprResult ConstraintSatisfactionChecker::EvaluateAtomicConstraint(
@@ -653,21 +669,7 @@ ExprResult 
ConstraintSatisfactionChecker::EvaluateAtomicConstraint(
         // A non-SFINAE error has occurred as a result of this
         // substitution.
         return ExprError();
-
-      PartialDiagnosticAt SubstDiag{SourceLocation(),
-                                    PartialDiagnostic::NullDiagnostic()};
-      Info.takeSFINAEDiagnostic(SubstDiag);
-      // FIXME: This is an unfortunate consequence of there
-      //  being no serialization code for PartialDiagnostics and the fact
-      //  that serializing them would likely take a lot more storage than
-      //  just storing them as strings. We would still like, in the
-      //  future, to serialize the proper PartialDiagnostic as serializing
-      //  it as a string defeats the purpose of the diagnostic mechanism.
-      Satisfaction.Details.emplace_back(
-          new (S.Context) ConstraintSubstitutionDiagnostic{
-              SubstDiag.first,
-              allocateStringFromConceptDiagnostic(S, SubstDiag.second)});
-      Satisfaction.IsSatisfied = false;
+      consumeSFINAEFailure(Info, Satisfaction);
       return ExprEmpty();
     }
   }
@@ -733,6 +735,8 @@ 
ConstraintSatisfactionChecker::SubstitutionInTemplateArguments(
           Constraint.getParameterMapping(), Constraint.getBeginLoc(), MLTAL,
           SubstArgs)) {
     Satisfaction.IsSatisfied = false;
+    if (Trap.hasErrorOccurred())
+      consumeSFINAEFailure(Info, Satisfaction);
     return std::nullopt;
   }
 
@@ -793,7 +797,7 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
       SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOutermost);
   if (!SubstitutedArgs) {
     Satisfaction.IsSatisfied = false;
-    return ExprEmpty();
+    return ExprError();
   }
 
   // Make sure that concepts are not evaluated in the context they are used,
@@ -827,7 +831,7 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
     Satisfaction.Details.emplace_back(
         new (S.Context) ConstraintSubstitutionDiagnostic{
             SubstitutedAtomicExpr.get()->getBeginLoc(),
-            allocateStringFromConceptDiagnostic(S, Msg)});
+            allocateStringFromConceptDiagnostic(Msg)});
     return SubstitutedAtomicExpr;
   }
 
@@ -1033,7 +1037,6 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
 
   if (!SubstitutedArgs) {
     Satisfaction.IsSatisfied = false;
-    // FIXME: diagnostics?
     return ExprError();
   }
 
@@ -1060,23 +1063,8 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
                                       OutArgs) ||
              Trap.hasErrorOccurred()) {
     Satisfaction.IsSatisfied = false;
-    if (!Trap.hasErrorOccurred())
-      return ExprError();
-
-    PartialDiagnosticAt SubstDiag{SourceLocation(),
-                                  PartialDiagnostic::NullDiagnostic()};
-    Info.takeSFINAEDiagnostic(SubstDiag);
-    // FIXME: This is an unfortunate consequence of there
-    //  being no serialization code for PartialDiagnostics and the fact
-    //  that serializing them would likely take a lot more storage than
-    //  just storing them as strings. We would still like, in the
-    //  future, to serialize the proper PartialDiagnostic as serializing
-    //  it as a string defeats the purpose of the diagnostic mechanism.
-    Satisfaction.Details.insert(
-        Satisfaction.Details.begin() + Size,
-        new (S.Context) ConstraintSubstitutionDiagnostic{
-            SubstDiag.first,
-            allocateStringFromConceptDiagnostic(S, SubstDiag.second)});
+    if (Trap.hasErrorOccurred())
+      consumeSFINAEFailure(Info, Satisfaction);
     return ExprError();
   }
 
@@ -1726,10 +1714,8 @@ bool Sema::EnsureTemplateArgumentListConstraints(
   llvm::SmallVector<AssociatedConstraint, 3> AssociatedConstraints;
   TD->getAssociatedConstraints(AssociatedConstraints);
   if (CheckConstraintSatisfaction(TD, AssociatedConstraints, TemplateArgsLists,
-                                  TemplateIDRange, Satisfaction))
-    return true;
-
-  if (!Satisfaction.IsSatisfied) {
+                                  TemplateIDRange, Satisfaction) ||
+      !Satisfaction.IsSatisfied) {
     SmallString<128> TemplateArgString;
     TemplateArgString = " ";
     TemplateArgString += getTemplateArgumentBindingsText(
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp 
b/clang/lib/Sema/SemaTemplateDeduction.cpp
index f177f0aa8645e..a554feae3dfb9 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -3987,9 +3987,8 @@ TemplateDeductionResult 
Sema::FinishTemplateArgumentDeduction(
     if (CheckFunctionTemplateConstraints(
             Info.getLocation(),
             FunctionTemplate->getCanonicalDecl()->getTemplatedDecl(),
-            CTAI.CanonicalConverted, Info.AssociatedConstraintsSatisfaction))
-      return TemplateDeductionResult::MiscellaneousDeductionFailure;
-    if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
+            CTAI.CanonicalConverted, Info.AssociatedConstraintsSatisfaction) ||
+        !Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
       Info.reset(Info.takeSugared(), TemplateArgumentList::CreateCopy(
                                          Context, CTAI.CanonicalConverted));
       return TemplateDeductionResult::ConstraintsNotSatisfied;
@@ -4035,10 +4034,8 @@ TemplateDeductionResult 
Sema::FinishTemplateArgumentDeduction(
   if (IsLambda && !IsIncomplete) {
     if (CheckFunctionTemplateConstraints(
             Info.getLocation(), Specialization, CTAI.CanonicalConverted,
-            Info.AssociatedConstraintsSatisfaction))
-      return TemplateDeductionResult::MiscellaneousDeductionFailure;
-
-    if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
+            Info.AssociatedConstraintsSatisfaction) ||
+        !Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
       Info.reset(Info.takeSugared(), TemplateArgumentList::CreateCopy(
                                          Context, CTAI.CanonicalConverted));
       return TemplateDeductionResult::ConstraintsNotSatisfied;
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
index 15e00e4481e75..97c0d2e57c004 100644
--- 
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
@@ -89,7 +89,9 @@ template<typename T> requires (T{}) // expected-error{{atomic 
constraint must be
 struct D { };
 
 static_assert(C<int>{}); // expected-note{{while checking constraint 
satisfaction for template 'C<int>' required here}}
+// expected-error@-1 {{constraints not satisfied for class template 'C'}}
 static_assert(D<int>{}); // expected-note{{while checking constraint 
satisfaction for template 'D<int>' required here}}
+// expected-error@-1 {{constraints not satisfied for class template 'D'}}
 
 // Test the delayed instantiation, the 'foo' implementation shouldn't cause the
 // constraint failure(or crash!) until the use to create 'y'.
diff --git a/clang/test/SemaCXX/cxx23-assume.cpp 
b/clang/test/SemaCXX/cxx23-assume.cpp
index a594a1a44337b..d2563c1c45107 100644
--- a/clang/test/SemaCXX/cxx23-assume.cpp
+++ b/clang/test/SemaCXX/cxx23-assume.cpp
@@ -129,6 +129,7 @@ struct F {
 template <typename T>
 constexpr int f5() requires C<T> { return 1; } // expected-note {{while 
checking the satisfaction}}
                                                // expected-note@-1 {{candidate 
template ignored}}
+                                               // expected-note@-2 {{because 
'T' does not satisfy 'C'}}
 
 template <typename T>
 constexpr int f5() requires (!C<T>) { return 2; } // expected-note 3 {{while 
checking the satisfaction}} \
diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp 
b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
index b7b03278da54e..d3e681f22ab26 100644
--- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
+++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
@@ -154,58 +154,84 @@ consteval int Or3() requires (C<typename T::type> || ... 
|| C<typename U::type>)
 static_assert(And1<>() == 1);
 static_assert(And1<S>() == 1);
 static_assert(And1<S, S>() == 1);
-// FIXME: The diagnostics are not so great
 static_assert(And1<int>() == 1); // expected-error {{no matching function for 
call to 'And1'}}
                                  // expected-note@#and1 {{candidate template 
ignored: constraints not satisfied [with T = <int>]}}
-                                 // expected-note@#and1 {{because substituted 
constraint expression is ill-formed: type 'int' cannot be used prior to '::' 
because it has no members}}
+                                 // expected-note@#and1 {{because 'typename 
T::type' does not satisfy 'C'}}
+                                 // expected-note@#C {{because 'T' does not 
satisfy 'A'}}
+                                 // expected-note@#C {{because substituted 
constraint expression is ill-formed: type 'int' cannot be used prior to '::' 
because it has no members}}
 
 static_assert(And1<S, int>() == 1); // expected-error {{no matching function 
for call to 'And1'}}
                                    // expected-note@#and1 {{candidate template 
ignored: constraints not satisfied [with T = <S, int>]}}
-                                   // expected-note@#and1 {{because 
substituted constraint expression is ill-formed: type 'int' cannot be used 
prior to '::' because it has no members}}
+                                   // expected-note@#and1 {{because 'typename 
T::type' does not satisfy 'C'}}
+                                   // expected-note@#C {{because 'T' does not 
satisfy 'A'}}
+                                   // expected-note@#C {{because substituted 
constraint expression is ill-formed: type 'int' cannot be used prior to '::' 
because it has no members}}
+
 
 static_assert(And1<int, S>() == 1); // expected-error {{no matching function 
for call to 'And1'}}
                                    // expected-note@#and1 {{candidate template 
ignored: constraints not satisfied [with T = <int, S>]}}
-                                   // expected-note@#and1 {{because 
substituted constraint expression is ill-formed: type 'int' cannot be used 
prior to '::' because it has no members}}
+                                   // expected-note@#and1 {{because 'typename 
T::type' does not satisfy 'C'}}
+                                   // expected-note@#C {{because 'T' does not 
satisfy 'A'}}
+                                   // expected-note@#C {{because substituted 
constraint expression is ill-formed: type 'int' cannot be used prior to '::' 
because it has no members}}
+
 
 static_assert(And2<S>() == 2);
 static_assert(And2<S, S>() == 2);
 static_assert(And2<int>() == 2);  // expected-error {{no matching function for 
call to 'And2'}}
                                   // expected-note@#and2 {{candidate template 
ignored: constraints not satisfied [with T = int, U = <>]}}
-                                  // expected-note@#and2 {{because substituted 
constraint expression is ill-formed: type 'int' cannot be used prior to '::' 
because it has no members}}
+                                  // expected-note@#and2 {{because 'typename 
U::type' does not satisfy 'C'}}
+                                  // expected-note@#C {{because 'T' does not 
satisfy 'A'}}
+                                  // expected-note@#C {{because substituted 
constraint expression is ill-formed: type 'int' cannot be used prior to '::' 
because it has no members}}
+
 
 
 static_assert(And2<int, int>() == 2);  // expected-error {{no matching 
function for call to 'And2'}}
                                       // expected-note@#and2 {{candidate 
template ignored: constraints not satisfied [with T = S, U = <int>]}} \
-                                      // expected-note@#and2 {{because 
substituted constraint expression is ill-formed: type 'int' cannot be used 
prior to '::' because it has no members}}
+                                      // expected-note@#and2 {{because 
'typename U::type' does not satisfy 'C'}}
+                                      // expected-note@#C {{because 'T' does 
not satisfy 'A'}}
+                                      // expected-note@#C {{because 
substituted constraint expression is ill-formed: type 'int' cannot be used 
prior to '::' because it has no members}}
+
 
 static_assert(And2<S, int>() == 2); // expected-error {{no matching function 
for call to 'And2'}}
                                    // expected-note@#and2 {{candidate template 
ignored: constraints not satisfied [with T = int, U = <S>]}}
-                                   // expected-note@#and2 {{because 
substituted constraint expression is ill-formed: type 'int' cannot be used 
prior to '::' because it has no members}}
+                                   // expected-note@#and2 {{because 'typename 
T::type' does not satisfy 'C'}}
+                                   // expected-note@#C {{because 'T' does not 
satisfy 'A'}}
+                                   // expected-note@#C {{because substituted 
constraint expression is ill-formed: type 'int' cannot be used prior to '::' 
because it has no members}}
+
 
 static_assert(And2<int, S>() == 2); // expected-error {{no matching function 
for call to 'And2'}}
                                    // expected-note@#and2 {{candidate template 
ignored: constraints not satisfied [with T = int, U = <int>]}}
-                                   // expected-note@#and2 {{because 
substituted constraint expression is ill-formed: type 'int' cannot be used 
prior to '::' because it has no members}}
+                                   // expected-note@#and2 {{because 'typename 
T::type' does not satisfy 'C'}}
+                                   // expected-note@#C {{because 'T' does not 
satisfy 'A'}}
+                                   // expected-note@#C {{because substituted 
constraint expression is ill-formed: type 'int' cannot be used prior to '::' 
because it has no members}}
 
 static_assert(And3<S>() == 3);
 static_assert(And3<S, S>() == 3);
 static_assert(And3<int>() == 3);   // expected-error {{no matching function 
for call to 'And3'}}
                                    // expected-note@#and3 {{candidate template 
ignored: constraints not satisfied [with T = int, U = <>]}}
-                                   // expected-note@#and3 {{because 
substituted constraint expression is ill-formed: type 'int' cannot be used 
prior to '::' because it has no members}}
+                                   // expected-note@#and3 {{because 'typename 
U::type' does not satisfy 'C'}}
+                                   // expected-note@#C {{because 'T' does not 
satisfy 'A'}}
+                                   // expected-note@#C {{because substituted 
constraint expression is ill-formed: type 'int' cannot be used prior to '::' 
because it has no members}}
 
 
 static_assert(And3<int, int>() == 3);  // expected-error {{no matching 
function for call to 'And3'}}
                                       // expected-note@#and3 {{candidate 
template ignored: constraints not satisfied [with T = int, U = <int>]}}
-                                      // expected-note@#and3 {{because 
substituted constraint expression is ill-formed: type 'int' cannot be used 
prior to '::' because it has no members}}
+                                      // expected-note@#and3 {{because 
'typename T::type' does not satisfy 'C'}}
+                                      // expected-note@#C {{because 'T' does 
not satisfy 'A'}}
+                                      // expected-note@#C {{because 
substituted constraint expression is ill-formed: type 'int' cannot be used 
prior to '::' because it has no members}}
 
 
 static_assert(And3<S, int>() == 3); // expected-error {{no matching function 
for call to 'And3'}}
                                    // expected-note@#and3 {{candidate template 
ignored: constraints not satisfied [with T = S, U = <int>]}}
-                                   // expected-note@#and3 {{because 
substituted constraint expression is ill-formed: type 'int' cannot be used 
prior to '::' because it has no members}}
+                                   // expected-note@#and3 {{because 'typename 
T::type' does not satisfy 'C'}}
+                                   // expected-note@#C {{because 'T' does not 
satisfy 'A'}}
+                                   // expected-note@#C {{because substituted 
constraint expression is ill-formed: type 'int' cannot be used prior to '::' 
because it has no members}}
 
 
 static_assert(And3<int, S>() == 3); // expected-error {{no matching function 
for call to 'And3'}}
                                    // expected-note@#and3 {{candidate template 
ignored: constraints not satisfied [with T = int, U = <S>]}}
-                                   // expected-note@#and3 {{because 
substituted constraint expression is ill-formed: type 'int' cannot be used 
prior to '::' because it has no members}}
+                                   // expected-note@#and3 {{because 'typename 
T::type' does not satisfy 'C'}}
+                                   // expected-note@#C {{because 'T' does not 
satisfy 'A'}}
+                                   // expected-note@#C {{because substituted 
constraint expression is ill-formed: type 'int' cannot be used prior to '::' 
because it has no members}}
 
 
 static_assert(Or1<>() == 1); // expected-error {{no matching function for call 
to 'Or1'}}
@@ -216,7 +242,9 @@ static_assert(Or1<S, int>() == 1);
 static_assert(Or1<S, S>() == 1);
 static_assert(Or1<int>() == 1); // expected-error {{no matching function for 
call to 'Or1'}}
                                 // expected-note@#or1 {{candidate template 
ignored: constraints not satisfied}}
-                                // expected-note@#or1 {{because substituted 
constraint expression is ill-formed: type 'int' cannot be used prior to '::' 
because it has no members}}
+                                // expected-note@#or1 {{because 'typename 
T::type' does not satisfy 'C'}}
+                                // expected-note@#C {{because 'T' does not 
satisfy 'A'}}
+                                // expected-note@#C {{because substituted 
constraint expression is ill-formed: type 'int' cannot be used prior to '::' 
because it has no members}}
 
 static_assert(Or2<S>() == 2);
 static_assert(Or2<int, S>() == 2);
@@ -224,14 +252,18 @@ static_assert(Or2<S, int>() == 2);
 static_assert(Or2<S, S>() == 2);
 static_assert(Or2<int>() == 2); // expected-error {{no matching function for 
call to 'Or2'}}
                                 // expected-note@#or2 {{candidate template 
ignored: constraints not satisfied [with T = int, U = <>]}}
-                                // expected-note@#or2 {{because substituted 
constraint expression is ill-formed: type 'int' cannot be used prior to '::' 
because it has no members}}
+                                // expected-note@#or2 {{because 'typename 
T::type' does not satisfy 'C'}}
+                                // expected-note@#C {{because 'T' does not 
satisfy 'A'}}
+                                // expected-note@#C {{because substituted 
constraint expression is ill-formed: type 'int' cannot be used prior to '::' 
because it has no members}}
 static_assert(Or3<S>() == 3);
 static_assert(Or3<int, S>() == 3);
 static_assert(Or3<S, int>() == 3);
 static_assert(Or3<S, S>() == 3);
 static_assert(Or3<int>() == 3); // expected-error {{no matching function for 
call to 'Or3'}}
                                 // expected-note@#or3 {{candidate template 
ignored: constraints not satisfied}}
-                                // expected-note@#or3 {{because substituted 
constraint expression is ill-formed: type 'int' cannot be used prior to '::' 
because it has no members}}
+                                // expected-note@#or3 {{because 'typename 
T::type' does not satisfy 'C'}}
+                                // expected-note@#C {{because 'T' does not 
satisfy 'A'}}
+                                // expected-note@#C {{because substituted 
constraint expression is ill-formed: type 'int' cannot be used prior to '::' 
because it has no members}}
 }
 
 namespace bool_conversion_break {
@@ -655,6 +687,7 @@ void g() {
   f<char, int, short>();
   // expected-error@-1 {{no matching function}}
   // expected-note@#GH218548_f {{constraints not satisfied}}
+  // expected-note@#GH218548_f {{does not satisfy 'same_as_impl'}}
   // expected-note@#GH218548_f {{invalid index}}
 }
 
diff --git a/clang/test/SemaTemplate/concepts.cpp 
b/clang/test/SemaTemplate/concepts.cpp
index b37e856fe84d0..7b88489307d98 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -1824,6 +1824,7 @@ namespace instantiation_dependent {
   template <class V> requires C<X<V&>> struct Y {};
   Y<void> y;
   // expected-error@-1 {{constraints not satisfied for class template 'Y' 
[with V = void]}}
+  // expected-note@-3  {{because 'X<V &>' (aka 'int') does not satisfy 'C'}} \
   // expected-note@-3  {{because substituted constraint expression is 
ill-formed: cannot form a reference to 'void'}}
 } // namespace instantiation_dependent
 
@@ -2069,6 +2070,9 @@ struct quantity {};
 
 auto x = quantity<reference<int>{}, int>{};
 // expected-error@-1 {{constraints not satisfied for class template 'quantity' 
[with V = reference<int>{}, $1 = int]}}
+// expected-note@-5 {{because 'representation_of<type-parameter-0-1, 
get_spec(V)>' evaluated to false}}
+// expected-note@-7 {{because 'decltype(V)' does not satisfy 'repr_impl'}}
+// expected-note@-7 {{because substituted constraint expression is ill-formed: 
non-type template argument is not a constant expression}}
 
 } // namespace CannotResolve1
 
diff --git a/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp 
b/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp
index 5809ef684bbf3..a2e0881e6c17e 100644
--- a/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp
+++ b/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp
@@ -29,6 +29,7 @@ namespace constant_evaluated {
   // expected-note@-1{{in instantiation of}} expected-note@-1{{while 
substituting}}
   using s = S<int>;
   // expected-note@-1 {{while checking}}
+  // expected-error@-2 {{constraints not satisfied}}
   template<typename T> void foo() requires f<int[1]> { };
   // expected-note@-1{{in instantiation}} expected-note@-1{{while 
substituting}} \
      expected-note@-1{{candidate template ignored}}
diff --git a/clang/test/SemaTemplate/instantiate-requires-expr.cpp 
b/clang/test/SemaTemplate/instantiate-requires-expr.cpp
index a5a18acd069f3..979cc4dd1a7b9 100644
--- a/clang/test/SemaTemplate/instantiate-requires-expr.cpp
+++ b/clang/test/SemaTemplate/instantiate-requires-expr.cpp
@@ -235,6 +235,7 @@ template <class T> requires(T{})
 constexpr bool e_v = true;
 
 static_assert(e_v<bool>);
+// expected-error@-1 {{constraints not satisfied for variable template 'e_v'}}
 
 } // namespace GH73885
 

>From bf37b21aa4a703991d1cd96f2a7d214e3cb4fe71 Mon Sep 17 00:00:00 2001
From: Younan Zhang <[email protected]>
Date: Tue, 25 Aug 2026 21:31:34 +0800
Subject: [PATCH 2/4] Fix libc++ test && explain more failures

---
 clang/lib/Sema/SemaConcept.cpp                |  3 ---
 .../constrant-satisfaction-conversions.cpp    |  4 ++++
 .../SemaCXX/concept-crash-on-diagnostic.cpp   |  3 +--
 clang/test/SemaTemplate/concepts.cpp          | 19 +++++++++++++++++++
 .../SemaTemplate/cxx2a-constraint-exprs.cpp   |  4 +++-
 5 files changed, 27 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index c6683180778be..7e54e42c47f72 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -1293,9 +1293,6 @@ static bool CheckConstraintSatisfaction(
           /*BuildExpression=*/ConvertedExpr != nullptr)
           .Evaluate(*C, TemplateArgsLists);
 
-  if (Res.isInvalid())
-    return true;
-
   if (Res.isUsable() && ConvertedExpr)
     *ConvertedExpr = Res.get();
 
diff --git 
a/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp
 
b/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp
index 6dea0c62fe686..92292faddb1df 100644
--- 
a/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp
+++ 
b/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp
@@ -56,6 +56,8 @@ template<typename T> concept NullTy = Nullptr<T>;
 // expected-error@-1{{atomic constraint must be of type 'bool' (found }}
 // expected-note@+1{{while checking the satisfaction}}
 static_assert(NullTy<int>);
+// expected-error@-1{{static assertion failed}}
+// expected-note@-2{{because 'int' does not satisfy 'NullTy'}}
 
 template<typename T>
 auto Struct = S<T>{};
@@ -64,3 +66,5 @@ template<typename T> concept StructTy = Struct<T>;
 // expected-error@-1{{atomic constraint must be of type 'bool' (found 
'S<int>')}}
 // expected-note@+1{{while checking the satisfaction}}
 static_assert(StructTy<int>);
+// expected-error@-1{{static assertion failed}}
+// expected-note@-2{{because 'int' does not satisfy 'StructTy'}}
diff --git a/clang/test/SemaCXX/concept-crash-on-diagnostic.cpp 
b/clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
index 6bf2f441e83c4..d3ac650fd8c82 100644
--- a/clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
+++ b/clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
@@ -34,8 +34,7 @@ void function() {
 // expected-note@#5 {{while substituting deduced template arguments into 
function template 'compare' [with IteratorL = Object *, IteratorR = Object *]}}
 
 // expected-note@#4 {{candidate template ignored: constraints not satisfied 
[with IteratorL = Object *, IteratorR = Object *]}}
-// We don't know exactly the substituted type for `lhs == rhs`, thus a 
placeholder 'expr-type' is emitted.
-// expected-note@#3 {{because 'convertible_to<expr-type, bool>' would be 
invalid}}
+// expected-note@#3 {{because 'convertible_to<bool, bool>' evaluated to false}}
 
 namespace GH131530 {
 
diff --git a/clang/test/SemaTemplate/concepts.cpp 
b/clang/test/SemaTemplate/concepts.cpp
index 7b88489307d98..cbf612c8a57b0 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -160,6 +160,8 @@ namespace NoConstantFolding {
   int n;
   template <class T> concept C = &n + 3 - 3 == &n; // expected-error 
{{non-constant expression}} expected-note {{cannot refer to element 3 of 
non-array object}}
   static_assert(C<void>); // expected-note {{while checking}}
+  // expected-error@-1 {{static assertion failed}}
+  // expected-note@-2 {{because 'void' does not satisfy 'C'}}
 }
 
 namespace PR50337 {
@@ -1545,6 +1547,23 @@ concept C = sizeof(T) == 42;
 
 static_assert( requires {{ &f } -> C;} ); // expected-error {{reference to 
overloaded function could not be resolved;}}
 // expected-error@-1 {{static assertion failed due to requirement 'requires { 
{ &f() } -> C; }'}}
+}
+
+namespace invalid_expression_in_instantiation {
+
+template <class T, class U>
+concept is_same = __is_same(T, U);
+
+template <class T, class U>
+concept is_same_2 = is_same<T&, U&>;
+
+template <class T, class U>
+constexpr bool is_same_value() {
+  return is_same_2<T, U>;
+}
+
+// This should be SFINAE rather than a hard error.
+static_assert(!is_same_value<void, void>());
 
 }
 
diff --git a/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp 
b/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp
index a2e0881e6c17e..b5960d6869484 100644
--- a/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp
+++ b/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp
@@ -39,10 +39,12 @@ namespace constant_evaluated {
   template<typename T> void bar() requires requires { requires f<int[2]>; } { 
};
   // expected-note@-1{{in instantiation}} \
      expected-note@-1{{while substituting}} \
+     expected-note@-1{{candidate template ignored}} \
      expected-note@-1 {{while checking the satisfaction of nested requirement}}
   int b = (bar<int>(), 0);
+  // expected-error@-1 {{no matching function}}
   template<typename T> struct M { static void foo() requires f<int[3]> { }; };
   // expected-note@-1{{in instantiation}} expected-note@-1{{while 
substituting}}
   int c = (M<int>::foo(), 0);
-  // expected-note@-1 {{while checking}}
+  // expected-note@-1 {{while checking}} expected-error@-1 {{constraints not 
satisfied}}
 }

>From ebf052cf8bf8fc86f8b54062647403c5f81eda19 Mon Sep 17 00:00:00 2001
From: Younan Zhang <[email protected]>
Date: Tue, 25 Aug 2026 21:32:01 +0800
Subject: [PATCH 3/4] Also diagnose RequiresExpr within static_asserts

---
 clang/include/clang/Sema/Sema.h               |  3 ++
 clang/lib/Sema/SemaConcept.cpp                | 28 +++++++++++--------
 clang/lib/Sema/SemaDeclCXX.cpp                |  4 +++
 clang/lib/Sema/SemaTypeTraits.cpp             |  1 -
 clang/test/SemaCXX/bool-increment-SFINAE.cpp  |  1 +
 clang/test/SemaTemplate/concepts.cpp          |  3 ++
 .../instantiate-requires-expr.cpp             |  2 +-
 7 files changed, 28 insertions(+), 14 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index bad4430f78b55..dcf112fd8eaa4 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -15075,6 +15075,9 @@ class Sema final : public SemaBase {
   DiagnoseUnsatisfiedConstraint(const ConceptSpecializationExpr 
*ConstraintExpr,
                                 bool First = true);
 
+  void DiagnoseUnsatisfiedRequiresExpr(const RequiresExpr *RequiresExpr,
+                                       bool First = true);
+
   const NormalizedConstraint *getNormalizedAssociatedConstraints(
       ConstrainedDeclOrNestedRequirement Entity,
       ArrayRef<AssociatedConstraint> AssociatedConstraints);
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 7e54e42c47f72..e03687f9d3910 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -2005,18 +2005,7 @@ static void 
diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
       break;
     }
   } else if (auto *RE = dyn_cast<RequiresExpr>(SubstExpr)) {
-    // FIXME: RequiresExpr should store dependent diagnostics.
-    for (concepts::Requirement *Req : RE->getRequirements())
-      if (!Req->isDependent() && !Req->isSatisfied()) {
-        if (auto *E = dyn_cast<concepts::ExprRequirement>(Req))
-          diagnoseUnsatisfiedRequirement(S, E, First);
-        else if (auto *T = dyn_cast<concepts::TypeRequirement>(Req))
-          diagnoseUnsatisfiedRequirement(S, T, First);
-        else
-          diagnoseUnsatisfiedRequirement(
-              S, cast<concepts::NestedRequirement>(Req), First);
-        break;
-      }
+    S.DiagnoseUnsatisfiedRequiresExpr(RE);
     return;
   } else if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(SubstExpr)) {
     // Drill down concept ids treated as atomic constraints
@@ -2061,6 +2050,21 @@ static void diagnoseUnsatisfiedConstraintExpr(
       S, cast<const class Expr *>(Record), First);
 }
 
+void Sema::DiagnoseUnsatisfiedRequiresExpr(const RequiresExpr *RE, bool First) 
{
+  // FIXME: RequiresExpr should store dependent diagnostics.
+  for (concepts::Requirement *Req : RE->getRequirements())
+    if (!Req->isDependent() && !Req->isSatisfied()) {
+      if (auto *E = dyn_cast<concepts::ExprRequirement>(Req))
+        diagnoseUnsatisfiedRequirement(*this, E, First);
+      else if (auto *T = dyn_cast<concepts::TypeRequirement>(Req))
+        diagnoseUnsatisfiedRequirement(*this, T, First);
+      else
+        diagnoseUnsatisfiedRequirement(
+            *this, cast<concepts::NestedRequirement>(Req), First);
+      break;
+    }
+}
+
 void Sema::DiagnoseUnsatisfiedConstraint(
     const ConstraintSatisfaction &Satisfaction, SourceLocation Loc,
     bool First) {
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 45e0cafeed040..4e90c496de342 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17841,6 +17841,8 @@ static bool UsefulToPrintExpr(const Expr *E) {
 }
 
 void Sema::DiagnoseStaticAssertDetails(const Expr *E) {
+  // FIXME: Should we also ignore explicit casts?
+  E = E->IgnoreParenImpCasts();
   if (const auto *Op = dyn_cast<BinaryOperator>(E);
       Op && Op->getOpcode() != BO_LOr) {
     const Expr *LHS = Op->getLHS()->IgnoreParenImpCasts();
@@ -17875,6 +17877,8 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) {
           << DiagSides[0].ValueString << Op->getOpcodeStr()
           << DiagSides[1].ValueString << Op->getSourceRange();
     }
+  } else if (const auto *RE = dyn_cast<RequiresExpr>(E)) {
+    DiagnoseUnsatisfiedRequiresExpr(RE);
   } else {
     DiagnoseTypeTraitDetails(E);
   }
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp 
b/clang/lib/Sema/SemaTypeTraits.cpp
index 351992873f236..cb30ff9fd02da 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -2752,7 +2752,6 @@ static void DiagnoseNonAbstractReason(Sema &SemaRef, 
SourceLocation Loc,
 }
 
 void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
-  E = E->IgnoreParenImpCasts();
   if (E->containsErrors())
     return;
 
diff --git a/clang/test/SemaCXX/bool-increment-SFINAE.cpp 
b/clang/test/SemaCXX/bool-increment-SFINAE.cpp
index 3a465fa5a3d5a..1265fca01ae51 100644
--- a/clang/test/SemaCXX/bool-increment-SFINAE.cpp
+++ b/clang/test/SemaCXX/bool-increment-SFINAE.cpp
@@ -26,6 +26,7 @@ concept can_increment = requires(T t) {
 template <class T>
 void f() {
   static_assert(requires(T t) { ++t; }); // cxx20-error {{static assertion 
failed due to requirement 'requires (bool t) { <<error-expression>>; }'}}
+  // cxx20-note@-1 {{because '++t' would be invalid}}
 }
 
 int main() {
diff --git a/clang/test/SemaTemplate/concepts.cpp 
b/clang/test/SemaTemplate/concepts.cpp
index cbf612c8a57b0..a4e737f97de7e 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -1547,6 +1547,9 @@ concept C = sizeof(T) == 42;
 
 static_assert( requires {{ &f } -> C;} ); // expected-error {{reference to 
overloaded function could not be resolved;}}
 // expected-error@-1 {{static assertion failed due to requirement 'requires { 
{ &f() } -> C; }'}}
+// expected-note@-2 {{because 'void' does not satisfy 'C'}}
+// expected-note@-5 {{invalid application of 'sizeof' to an incomplete type 
'void'}}
+
 }
 
 namespace invalid_expression_in_instantiation {
diff --git a/clang/test/SemaTemplate/instantiate-requires-expr.cpp 
b/clang/test/SemaTemplate/instantiate-requires-expr.cpp
index 979cc4dd1a7b9..ca768195f3b35 100644
--- a/clang/test/SemaTemplate/instantiate-requires-expr.cpp
+++ b/clang/test/SemaTemplate/instantiate-requires-expr.cpp
@@ -327,7 +327,7 @@ namespace SusbtitutionFailureInArguments1 {
   template void g<int>();
   // expected-error@-1 {{does not refer to a function template}}
 
-  // FIXME: static_assert should gain support for explaining non-satisfied 
requirements.
   static_assert(requires { { 0 } -> C; });
   // expected-error@-1 {{static assertion failed due to requirement 'requires 
{ { <<error-expression>> } -> C; }'}}
+  // expected-note@-2 {{because 'C<expr-type>' would be invalid}}
 } // namespace SubstitutionFailureInArguments1

>From 7c43710040e38cdc473357740dc9508edce736b3 Mon Sep 17 00:00:00 2001
From: Younan Zhang <[email protected]>
Date: Tue, 25 Aug 2026 21:41:03 +0800
Subject: [PATCH 4/4] Release note && a fixup

---
 clang/docs/ReleaseNotes.md     | 2 ++
 clang/lib/Sema/SemaConcept.cpp | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 8cc8eb5f80066..7ac68a1684e72 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -421,6 +421,8 @@ features cannot lower the translation-unit ABI level;
 - `-Wc++98-compat` now diagnoses explicit conversion functions in C++20 and
   later, matching the behavior in C++11 through C++17. (#GH161689)
 
+- Clang now diagnoses more details when a constraint evaluates to false.
+
 ### Improvements to Clang's time-trace
 
 ### Improvements to Coverage Mapping
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index e03687f9d3910..d2b7759e95244 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -2005,7 +2005,7 @@ static void 
diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
       break;
     }
   } else if (auto *RE = dyn_cast<RequiresExpr>(SubstExpr)) {
-    S.DiagnoseUnsatisfiedRequiresExpr(RE);
+    S.DiagnoseUnsatisfiedRequiresExpr(RE, First);
     return;
   } else if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(SubstExpr)) {
     // Drill down concept ids treated as atomic constraints

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to