https://github.com/zyn0217 created 
https://github.com/llvm/llvm-project/pull/210498

This facilitates debugging with concepts, particularly when we need to check 
complex parameter mappings.

Taking an example from cxx2c-fold-exprs.cpp:

```cpp

template <class T> concept A = (T(), true);
template <class T> concept C = A<T> && true; // #C

struct S {
  using type = int;
};

template <typename T, typename... U>
consteval int And3() requires (C<typename T::type> && ... && C<typename 
U::type>) { // #and3
    return 3;
}
static_assert(And3<S, S>() == 3);

```

It gives us:

```cpp
CompoundConstraint 0x556cfdb54718 <cxx2c-fold-exprs.cpp:9:32, col:80> 
Conjunction
|-ConceptIdConstraint 0x556cfdb54520 <col:32, col:50> C<typename T::type>
| `-CompoundConstraint 0x556cfdb544d8 <line:2:32, col:40> Conjunction
|   |-ConceptIdConstraint 0x556cfdb54448 <col:32, col:35> A<T>
|   | |-ParameterMapping Indexes={0} IndexesForSubsumption={}
|   | | `-#0: <class T> -> typename T::type
|   | |   `-TemplateArgument type 0x556cfdb547f8
|   | `-AtomicConstraint 0x556cfdb54400 <line:1:33, col:38> T() , true
|   |   `-ParameterMapping Indexes={0} IndexesForSubsumption={0}
|   |     `-#0: <class T> -> typename T::type
|   |       `-TemplateArgument type 0x556cfdb548d8
|   `-AtomicConstraint 0x556cfdb54490 <line:2:40> true
`-FoldExpandedConstraint 0x556cfdb546d0 <line:9:62, col:80> And C<typename 
U::type>
  `-ConceptIdConstraint 0x556cfdb54688 <col:62, col:80> C<typename U::type>
    `-CompoundConstraint 0x556cfdb54640 <line:2:32, col:40> Conjunction
      |-ConceptIdConstraint 0x556cfdb545b0 <col:32, col:35> A<T>
      | |-ParameterMapping Indexes={0} IndexesForSubsumption={}
      | | `-#0: <class T> -> typename U::type
      | |   `-TemplateArgument type 0x556cfdb54ab8
      | `-AtomicConstraint 0x556cfdb54568 <line:1:33, col:38> T() , true
      |   `-ParameterMapping Indexes={0} IndexesForSubsumption={0}
      |     `-#0: <class T> -> typename U::type
      |       `-TemplateArgument type 0x556cfdb54b98
      `-AtomicConstraint 0x556cfdb545f8 <line:2:40> true
```

Since this is mainly for debugging purpose, we don't promise any text stability 
and hence no tests provided.

>From 21c7841860e3e8a3dcb1f3eaf97b7dac945bb6e5 Mon Sep 17 00:00:00 2001
From: Younan Zhang <[email protected]>
Date: Sat, 18 Jul 2026 17:38:57 +0800
Subject: [PATCH] [Clang] Add NormalizedConstraint::dump()

This facilitates debugging with NormalizedConstraints, particularly when
checking parameter mappings.
---
 clang/include/clang/AST/TemplateBase.h |   2 +
 clang/include/clang/Sema/SemaConcept.h |   3 +
 clang/lib/AST/TemplateBase.cpp         |  26 +++++
 clang/lib/Sema/SemaConcept.cpp         | 150 +++++++++++++++++++++++++
 4 files changed, 181 insertions(+)

diff --git a/clang/include/clang/AST/TemplateBase.h 
b/clang/include/clang/AST/TemplateBase.h
index c0db55ea92cd5..f7d754ea6082b 100644
--- a/clang/include/clang/AST/TemplateBase.h
+++ b/clang/include/clang/AST/TemplateBase.h
@@ -295,6 +295,8 @@ class TemplateArgument {
   /// Return the kind of stored template argument.
   ArgKind getKind() const { return (ArgKind)TypeOrValue.Kind; }
 
+  StringRef getKindName() const;
+
   /// Determine whether this template argument has no value.
   bool isNull() const { return getKind() == Null; }
 
diff --git a/clang/include/clang/Sema/SemaConcept.h 
b/clang/include/clang/Sema/SemaConcept.h
index bdd997b18cb08..4bf251d351f06 100644
--- a/clang/include/clang/Sema/SemaConcept.h
+++ b/clang/include/clang/Sema/SemaConcept.h
@@ -276,6 +276,9 @@ struct NormalizedConstraint {
 
   SourceRange getSourceRange() const { return {getBeginLoc(), getEndLoc()}; }
 
+  void dump(ASTContext &Context) const;
+  void dump(llvm::raw_ostream &OS, ASTContext &Context) const;
+
 private:
   friend class Sema;
   static NormalizedConstraint *
diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp
index a5e0dde45364c..d64d3bd5dcc30 100644
--- a/clang/lib/AST/TemplateBase.cpp
+++ b/clang/lib/AST/TemplateBase.cpp
@@ -258,6 +258,32 @@ TemplateArgument::CreatePackCopy(ASTContext &Context,
   return TemplateArgument(Args.copy(Context));
 }
 
+StringRef TemplateArgument::getKindName() const {
+  switch (getKind()) {
+  case TemplateArgument::Null:
+    return "null";
+  case TemplateArgument::Type:
+    return "type";
+  case TemplateArgument::Declaration:
+    return "decl";
+  case TemplateArgument::NullPtr:
+    return "nullptr";
+  case TemplateArgument::Integral:
+    return "integral";
+  case TemplateArgument::Template:
+    return "template";
+  case TemplateArgument::TemplateExpansion:
+    return "template expansion";
+  case TemplateArgument::Expression:
+    return "expression";
+  case TemplateArgument::Pack:
+    return "pack";
+  case TemplateArgument::StructuralValue:
+    return "structural value";
+  }
+  llvm_unreachable("unhandled ArgKind");
+}
+
 TemplateArgumentDependence TemplateArgument::getDependence() const {
   auto Deps = TemplateArgumentDependence::None;
   switch (getKind()) {
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 8831a26224e7d..98d3951c491b0 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -17,6 +17,7 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/ExprConcepts.h"
 #include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/TextNodeDumper.h"
 #include "clang/Basic/OperatorPrecedence.h"
 #include "clang/Sema/EnterExpressionEvaluationContext.h"
 #include "clang/Sema/Initialization.h"
@@ -30,6 +31,7 @@
 #include "llvm/ADT/PointerUnion.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/SaveAndRestore.h"
+#include "llvm/Support/ScopedPrinter.h"
 #include "llvm/Support/TimeProfiler.h"
 
 using namespace clang;
@@ -2965,3 +2967,151 @@ bool SubsumptionChecker::Subsumes(Literal A, Literal B) 
{
   }
   llvm_unreachable("unknown literal kind");
 }
+
+namespace {
+
+class DumpNormalizedConstraint {
+  raw_ostream &OS;
+  const PrintingPolicy &PP;
+  TextNodeDumper TD;
+
+public:
+  DumpNormalizedConstraint(raw_ostream &OS, ASTContext &Context)
+      : OS(OS), PP(Context.getPrintingPolicy()),
+        TD(OS, Context, /*ShowColors=*/false) {}
+
+  void dump(const NormalizedConstraint &N) {
+    TD.AddChild([&] { Traverse(N); });
+  }
+
+private:
+  void Traverse(const NormalizedConstraint &N) {
+    switch (N.getKind()) {
+    case NormalizedConstraint::ConstraintKind::Compound:
+      VisitCompound(static_cast<const CompoundConstraint &>(N));
+      break;
+    case NormalizedConstraint::ConstraintKind::Atomic:
+      VisitAtomic(static_cast<const AtomicConstraint &>(N));
+      break;
+    case NormalizedConstraint::ConstraintKind::ConceptId:
+      VisitConceptId(static_cast<const ConceptIdConstraint &>(N));
+      break;
+    case NormalizedConstraint::ConstraintKind::FoldExpanded:
+      VisitFoldExpanded(static_cast<const FoldExpandedConstraint &>(N));
+      break;
+    }
+  }
+
+  void WriteNodeHeader(const NormalizedConstraint &N, StringRef Kind) {
+    OS << Kind;
+    TD.dumpPointer(&N);
+    TD.dumpSourceRange(N.getSourceRange());
+  }
+
+  void WritePackIndex(const NormalizedConstraintWithParamMapping &N) {
+    if (auto Idx = N.getPackSubstitutionIndex())
+      OS << " SubstIndex=" << *Idx;
+  }
+
+  void VisitCompound(const CompoundConstraint &C) {
+    WriteNodeHeader(C, "CompoundConstraint");
+    OS << " "
+       << (C.getCompoundKind() == NormalizedConstraint::CCK_Conjunction
+               ? "Conjunction"
+               : "Disjunction");
+    TD.AddChild([&] { Traverse(C.getLHS()); });
+    TD.AddChild([&] { Traverse(C.getRHS()); });
+  }
+
+  void VisitAtomic(const AtomicConstraint &A) {
+    WriteNodeHeader(A, "AtomicConstraint");
+    WritePackIndex(A);
+    OS << " ";
+    A.getConstraintExpr()->printPretty(OS, /*Helper=*/nullptr, PP);
+    WriteParameterMapping(A);
+  }
+
+  void VisitConceptId(const ConceptIdConstraint &C) {
+    WriteNodeHeader(C, "ConceptIdConstraint");
+    WritePackIndex(C);
+    OS << " ";
+    if (auto *CSE = C.getConceptSpecializationExpr()) {
+      CSE->printPretty(OS, /*Helper=*/nullptr, PP);
+    } else {
+      C.getConceptId()->print(OS, PP);
+    }
+    WriteParameterMapping(C);
+    TD.AddChild([&] { Traverse(C.getNormalizedConstraint()); });
+  }
+
+  void VisitFoldExpanded(const FoldExpandedConstraint &F) {
+    WriteNodeHeader(F, "FoldExpandedConstraint");
+    OS << " "
+       << (F.getFoldOperator() == FoldExpandedConstraint::FoldOperatorKind::And
+               ? "And"
+               : "Or");
+    WritePackIndex(F);
+    OS << " ";
+    F.getPattern()->printPretty(OS, /*Helper=*/nullptr, PP);
+    WriteParameterMapping(F);
+    TD.AddChild([&] { Traverse(F.getNormalizedPattern()); });
+  }
+
+  void WriteParameterMapping(const NormalizedConstraintWithParamMapping &N) {
+    if (!N.hasParameterMapping() || N.mappingOccurenceList().none())
+      return;
+    TD.AddChild([this, Indexes(N.mappingOccurenceList()),
+                 IndexesForSub(N.mappingOccurenceListForSubsumption()),
+                 Mapping(N.getParameterMapping()),
+                 TPL(N.getUsedTemplateParamList())] {
+      OS << "ParameterMapping";
+      WriteOccurenceList("Indexes", Indexes);
+      WriteOccurenceList("IndexesForSubsumption", IndexesForSub);
+      unsigned Slot = 0;
+      for (unsigned ParamIndex : Indexes.set_bits()) {
+        TD.AddChild([this, Slot, ParamIndex, Mapping, TPL] {
+          OS << "#" << ParamIndex << ": ";
+          if (TPL && Slot < TPL->size()) {
+            const NamedDecl *Param = TPL->getParam(Slot);
+            OS << "<";
+            Param->print(OS, PP);
+            OS << ">";
+          }
+          OS << " -> ";
+          Mapping[Slot].getArgument().print(PP, OS,
+                                            /*IncludeType=*/false);
+          TD.AddChild([this, Slot, Mapping] {
+            const TemplateArgument &TA = Mapping[Slot].getArgument();
+            OS << "TemplateArgument " << TA.getKindName();
+            TD.dumpPointer(&TA);
+          });
+        });
+        ++Slot;
+      }
+    });
+  }
+
+  void WriteOccurenceList(StringRef Label,
+                          const NormalizedConstraint::OccurenceList &BV) {
+    if (BV.none())
+      return;
+    OS << " " << Label << "={"
+       << llvm::join(
+              llvm::map_range(
+                  llvm::make_range(BV.set_bits_begin(), BV.set_bits_end()),
+                  [](unsigned I) { return llvm::to_string(I); }),
+              ", ")
+       << '}';
+  }
+};
+
+} // namespace
+
+LLVM_DUMP_METHOD void NormalizedConstraint::dump(ASTContext &Context) const {
+  dump(llvm::errs(), Context);
+}
+
+LLVM_DUMP_METHOD void NormalizedConstraint::dump(llvm::raw_ostream &OS,
+                                                 ASTContext &Context) const {
+  return DumpNormalizedConstraint(OS, Context).dump(*this);
+}

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

Reply via email to