[clang] [Clang] Fix an iterator invalidation bug in concept normalization cache (PR #165352)
Younan Zhang via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 27 22:56:26 PDT 2025
https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/165352
>From 855c54c8639e9bd790bc4966eb633b3a2e046f4d Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 28 Oct 2025 12:57:58 +0800
Subject: [PATCH 1/2] [Clang] Fix an iterator invalidation bug in concept
normalization cache
The NormalizationCache may be inserted recursively when normalizing
template arguments with non-dependent default arguments. Since the ADT
doesn't preserve iterator validity, this caused undefined behavior.
---
clang/lib/Sema/SemaConcept.cpp | 8 ++++++--
clang/test/SemaTemplate/concepts.cpp | 26 ++++++++++++++++++++++++++
2 files changed, 32 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index f04cc454cdb7c..d101227770a90 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -2408,11 +2408,15 @@ const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints(
if (CacheEntry == NormalizationCache.end()) {
auto *Normalized = NormalizedConstraint::fromAssociatedConstraints(
*this, ND, AssociatedConstraints);
+ if (!Normalized) {
+ NormalizationCache.try_emplace(ConstrainedDeclOrNestedReq, nullptr);
+ return nullptr;
+ }
+ bool Failed = SubstituteParameterMappings(*this).substitute(*Normalized);
CacheEntry =
NormalizationCache.try_emplace(ConstrainedDeclOrNestedReq, Normalized)
.first;
- if (!Normalized ||
- SubstituteParameterMappings(*this).substitute(*Normalized))
+ if (Failed)
return nullptr;
}
return CacheEntry->second;
diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp
index becf5467a1b61..c90af41a09468 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -1632,3 +1632,29 @@ void fn3() {
}
}
+
+namespace GH165238 {
+
+namespace std {
+template <typename, typename _Tp>
+concept output_iterator = requires(_Tp __t) { __t; };
+template <typename _Out> struct basic_format_context {
+ static_assert(output_iterator<_Out, int>);
+ using char_type = _Out;
+};
+template <typename> class basic_format_parse_context;
+template <typename, typename _Context, typename _Formatter,
+ typename = basic_format_parse_context<typename _Context::char_type>>
+concept __parsable_with = requires(_Formatter __f) { __f; };
+template <typename _Tp, typename _CharT,
+ typename _Context = basic_format_context<_CharT>>
+concept __formattable_impl = __parsable_with<_Tp, _Context, _Context>;
+template <typename _Tp, typename _CharT>
+concept formattable = __formattable_impl<_Tp, _CharT>;
+} // namespace std
+struct {
+ void operator()(std::formattable<char> auto);
+} call;
+void foo() { call(""); }
+
+}
>From fed680164ae6f8b8161dd2a2cb52e5cc7344d410 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 28 Oct 2025 13:52:29 +0800
Subject: [PATCH 2/2] Add comment
---
clang/lib/Sema/SemaConcept.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index d101227770a90..fb4d0b4582684 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -2412,6 +2412,7 @@ const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints(
NormalizationCache.try_emplace(ConstrainedDeclOrNestedReq, nullptr);
return nullptr;
}
+ // substitute() can invalidate iterators of NormalizationCache.
bool Failed = SubstituteParameterMappings(*this).substitute(*Normalized);
CacheEntry =
NormalizationCache.try_emplace(ConstrainedDeclOrNestedReq, Normalized)
More information about the cfe-commits
mailing list