https://github.com/lux-QAQ created
https://github.com/llvm/llvm-project/pull/198168
This is related to #143129. The immediate leak reported there was addressed by
explicitly destroying the `ConstraintSatisfaction` stored in `CNSInfo`.
`DeductionFailureInfo` currently stores `ConstraintsNotSatisfied` diagnostic
payloads as a `ConstraintSatisfaction` inside `CNSInfo`.
However, `CNSInfo` is allocated in `ASTContext` arena storage. `ASTContext`
will reclaim the raw memory, but it will not automatically run C++ destructors
for objects allocated into that arena. This is awkward for
`ConstraintSatisfaction`, which owns `SmallVector` members and therefore may
need destruction if those vectors grow out of their inline storage.
This patch changes `CNSInfo` to store a pointer to a const
`ASTConstraintSatisfaction` instead. That type is already the flattened,
AST-safe representation of constraint satisfaction data. As noted in
`ASTConcept.h`, it is safe to store in AST storage, unlike
`ConstraintSatisfaction`.
This avoids keeping a non-trivially-destructible `ConstraintSatisfaction`
object inside `CNSInfo`, and reuses the existing
`ASTConstraintSatisfaction::Create` logic, including the deep-copy handling for
substitution diagnostics.
## Details
The `ConstraintsNotSatisfied` payload now stores a pointer instead of embedding
a full `ConstraintSatisfaction` object:
```cpp
TemplateArgumentList *TemplateArgs;
const ASTConstraintSatisfaction *Satisfaction;
```
This patch also adds an overload to `Sema::DiagnoseUnsatisfiedConstraint`
accepting `const ASTConstraintSatisfaction *`, so overload diagnostics can
consume the flattened representation directly.
`static_assert(std::is_trivially_destructible_v<CNSInfo>)` was omitted to avoid
adding an otherwise unnecessary `<type_traits>` include.
## Memory Impact
This change was motivated by the lifetime mismatch around `CNSInfo` and
`ConstraintSatisfaction`, but it also reduces the size of each
constraints-not-satisfied payload. In particular, `CNSInfo` no longer embeds
the inline storage of the `SmallVector`s inside `ConstraintSatisfaction`.
On synthetic constrained-overload stress cases, peak RSS was lower than a
baseline Release build:
| Case | Baseline | This patch | Change |
| --- | --- | --- | --- |
| `100x100` | 56,576 KB | 55,040 KB | -2.71% |
| `200x200` | 67,072 KB | 61,184 KB | -8.78% |
| `500x500` | 134,528 KB | 99,022 KB | -26.39% |
| `1000x500` | 215,900 KB | 145,324 KB | -32.69% |
| `1000x1000` | 372,572 KB | 231,660 KB | -37.82% |
| `many_200x200x8` | 67,968 KB | 62,464 KB | -8.10% |
Both the baseline and the patched compiler were built and measured directly on
top of commit `bae540bde882aa1911ccffb0fe7691cb16831017`.
## RSS Stress Reproducer
The stress code generator used:
```python
#!/usr/bin/env python3
import argparse
import os
ap = argparse.ArgumentParser()
ap.add_argument("--candidates", type=int, required=True)
ap.add_argument("--calls", type=int, required=True)
ap.add_argument("-o", "--output", required=True)
args = ap.parse_args()
tmp = args.output + ".tmp"
with open(tmp, "w") as out:
def p(s=""):
print(s, file=out)
p("#include <type_traits>")
p("template<int I> struct Tag {};")
p("")
for i in range(args.candidates):
p("template<class T>")
p(f"concept Fail{i} = (sizeof(T) == {1000000 + i});")
p("template<class T>")
p(f" requires Fail{i}<T>")
p(f"void f(T, Tag<{i}>* = nullptr) {{}}")
p("")
p("void f(...) {}")
p("")
p("void test() {")
for i in range(args.calls):
p(f" f({i});")
p("}")
p("int main() { test(); return 0; }")
os.replace(tmp, args.output)
```
Collected via:
```bash
BASELINE=/home/lux/code/build-baseline/bin/clang++
PR=/home/lux/code/build-pr/bin/clang++
$BASELINE -v
$PR -v
for pair in "100 100" "200 200" "500 500" "1000 500" "1000 1000"; do
set -- $pair
c=$1
n=$2
src="stress_${c}_${n}.cpp"
./gen_constraint_stress.py --candidates "$c" --calls "$n" -o "$src"
for compiler_name in baseline pr; do
if [ "$compiler_name" = baseline ]; then
clang="$BASELINE"
else
clang="$PR"
fi
"$clang" -std=c++20 -fsyntax-only -Wno-everything "$src" >/dev/null
for i in $(seq 1 5); do
/usr/bin/time -f "rss_kb=%M elapsed_s=%e exit=%x" \
"$clang" -std=c++20 -fsyntax-only -Wno-everything "$src" \
>/dev/null
done
done
done
```
## Testing
```text
./build-pr/tools/clang/unittests/Sema/SemaTests
Passed: 67 tests.
```
```text
llvm-lit -sv -j20 build-pr/tools/clang/test --filter='Clang ::
(Sema|SemaCXX|SemaTemplate)/|Clang :: CXX/(temp|over)/'
No unexpected failures.
```
```text
ninja -C /home/lux/code/build-pr -j30 check-clang
No unexpected failures.
```
`check-clang` results:
```text
-- Testing: 24679 tests, 32 workers --
Testing: 0.. 10.. 20.. 30.. 40.. 50.. 60.. 70.. 80.. 90..
Testing Time: 143.19s
Total Discovered Tests: 52090
Skipped : 10 (0.02%)
Unsupported : 5681 (10.91%)
Passed : 46374 (89.03%)
Expectedly Failed: 25 (0.05%)
```
>From cfcb81097cd66428e42ef09edcc1eb2c91760933 Mon Sep 17 00:00:00 2001
From: lijinsong2025 <[email protected]>
Date: Sun, 17 May 2026 15:42:01 +0800
Subject: [PATCH] [Clang][Sema] Use ASTConstraintSatisfaction for
constraints-not-satisfied deduction info
---
clang/include/clang/Sema/Sema.h | 3 +++
clang/lib/Sema/SemaConcept.cpp | 14 ++++++++------
clang/lib/Sema/SemaOverload.cpp | 17 ++++++++++-------
3 files changed, 21 insertions(+), 13 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 5202244cee2a7..194884e9461fe 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -15075,6 +15075,9 @@ class Sema final : public SemaBase {
void DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction
&Satisfaction,
SourceLocation Loc = {},
bool First = true);
+ void
+ DiagnoseUnsatisfiedConstraint(const ASTConstraintSatisfaction &Satisfaction,
+ SourceLocation Loc = {}, bool First = true);
/// \brief Emit diagnostics explaining why a constraint expression was deemed
/// unsatisfied.
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index ac1c716b5c385..61f9048b678df 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -2007,16 +2007,18 @@ void Sema::DiagnoseUnsatisfiedConstraint(
}
void Sema::DiagnoseUnsatisfiedConstraint(
- const ConceptSpecializationExpr *ConstraintExpr, bool First) {
-
- const ASTConstraintSatisfaction &Satisfaction =
- ConstraintExpr->getSatisfaction();
+ const ASTConstraintSatisfaction &Satisfaction, SourceLocation Loc,
+ bool First) {
assert(!Satisfaction.IsSatisfied &&
"Attempted to diagnose a satisfied constraint");
+ ::DiagnoseUnsatisfiedConstraint(*this, Satisfaction.records(), Loc, First);
+}
- ::DiagnoseUnsatisfiedConstraint(*this, Satisfaction.records(),
- ConstraintExpr->getBeginLoc(), First);
+void Sema::DiagnoseUnsatisfiedConstraint(
+ const ConceptSpecializationExpr *ConstraintExpr, bool First) {
+ DiagnoseUnsatisfiedConstraint(ConstraintExpr->getSatisfaction(),
+ ConstraintExpr->getBeginLoc(), First);
}
namespace {
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index e11bbd7085798..af4d62e494c2e 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "CheckExprLifetime.h"
+#include "clang/AST/ASTConcept.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/Decl.h"
@@ -741,7 +742,7 @@ namespace {
// unsatisfied constraints.
struct CNSInfo {
TemplateArgumentList *TemplateArgs;
- ConstraintSatisfaction Satisfaction;
+ const ASTConstraintSatisfaction *Satisfaction;
};
}
@@ -822,9 +823,10 @@ clang::MakeDeductionFailureInfo(ASTContext &Context,
break;
case TemplateDeductionResult::ConstraintsNotSatisfied: {
- CNSInfo *Saved = new (Context) CNSInfo;
- Saved->TemplateArgs = Info.takeSugared();
- Saved->Satisfaction = std::move(Info.AssociatedConstraintsSatisfaction);
+ auto *Saved = new (Context)
+ CNSInfo{Info.takeSugared(),
+ ASTConstraintSatisfaction::Create(
+ Context, Info.AssociatedConstraintsSatisfaction)};
Result.Data = Saved;
break;
}
@@ -872,7 +874,8 @@ void DeductionFailureInfo::Destroy() {
case TemplateDeductionResult::ConstraintsNotSatisfied:
// FIXME: Destroy the template argument list?
- static_cast<CNSInfo *>(Data)->Satisfaction.~ConstraintSatisfaction();
+ // CNSInfo and ASTConstraintSatisfaction are ASTContext-allocated and do
+ // not own non-arena resources.
Data = nullptr;
if (PartialDiagnosticAt *Diag = getSFINAEDiagnostic()) {
Diag->~PartialDiagnosticAt();
@@ -11386,7 +11389,7 @@ bool
OverloadCandidate::NotValidBecauseConstraintExprHasError() const {
static_cast<TemplateDeductionResult>(DeductionFailure.Result) ==
TemplateDeductionResult::ConstraintsNotSatisfied &&
static_cast<CNSInfo *>(DeductionFailure.Data)
- ->Satisfaction.ContainsErrors;
+ ->Satisfaction->ContainsErrors;
}
void OverloadCandidateSet::AddDeferredTemplateCandidate(
@@ -12523,7 +12526,7 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl
*Found, Decl *Templated,
<< TemplateArgString;
S.DiagnoseUnsatisfiedConstraint(
- static_cast<CNSInfo*>(DeductionFailure.Data)->Satisfaction);
+ *static_cast<CNSInfo *>(DeductionFailure.Data)->Satisfaction);
return;
}
case TemplateDeductionResult::TooManyArguments:
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits