[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