From 855c54c8639e9bd790bc4966eb633b3a2e046f4d Mon Sep 17 00:00:00 2001 From: Younan Zhang 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 +concept output_iterator = requires(_Tp __t) { __t; }; +template struct basic_format_context { + static_assert(output_iterator<_Out, int>); + using char_type = _Out; +}; +template class basic_format_parse_context; +template > +concept __parsable_with = requires(_Formatter __f) { __f; }; +template > +concept __formattable_impl = __parsable_with<_Tp, _Context, _Context>; +template +concept formattable = __formattable_impl<_Tp, _CharT>; +} // namespace std +struct { + void operator()(std::formattable auto); +} call; +void foo() { call(""); } + +} From fed680164ae6f8b8161dd2a2cb52e5cc7344d410 Mon Sep 17 00:00:00 2001 From: Younan Zhang 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)