[clang] [alpha.webkit.NoDeleteChecker] Handle CXXStdInitializerListExpr in trivial analysis (PR #224723)

Ryosuke Niwa via cfe-commits cfe-commits at lists.llvm.org
Sat Oct 3 22:38:42 PDT 2026


https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/224723

>From 3087596110251a8c45e71c729c0025896fc7cad5 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Fri, 18 Sep 2026 12:56:37 -0700
Subject: [PATCH] [alpha.webkit.NoDeleteChecker] Handle
 CXXStdInitializerListExpr in trivial analysis

TrivialFunctionAnalysisVisitor had no handler for CXXStdInitializerListExpr,
so a braced list bound to a std::initializer_list fell through to VisitStmt
and was conservatively treated as non-trivial. This made any nodelete
function containing e.g. std::min({a, b, c}) report that it "contains code
that could destruct an object".

The backing array of a std::initializer_list is a temporary whose lifetime
ends in the enclosing function, so its elements really are destructed there.
Accept the node when the array's element type is trivially destructible and
recurse into the initializers, and keep rejecting it otherwise.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp     | 11 ++++
 .../Checkers/WebKit/nodelete-annotation.cpp   | 52 +++++++++++++++++++
 2 files changed, 63 insertions(+)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 1cd2822575845..0af17ac7fd533 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -1262,6 +1262,17 @@ class TrivialFunctionAnalysisVisitor
     return true;
   }
 
+  bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *ILE) {
+    auto *SubExpr = ILE->getSubExpr();
+    if (!SubExpr)
+      return false;
+    // The backing array of a std::initializer_list is a temporary whose
+    // lifetime ends in this function, so its elements are destructed here.
+    if (!CanTriviallyDestruct(SubExpr->getType()))
+      return false;
+    return Visit(SubExpr);
+  }
+
   bool VisitMemberExpr(const MemberExpr *ME) {
     // Field access is allowed but the base pointer may itself be non-trivial.
     return Visit(ME->getBase());
diff --git a/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp b/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp
index 11cf81c6bf0bc..fd11fcbd33772 100644
--- a/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp
@@ -850,3 +850,55 @@ namespace nodelete_ptrconversion {
   }
 
 } // namespace nodelete_ptrconversion
+
+namespace std {
+
+// The compiler only recognises the real ::std::initializer_list, so this mock
+// has to live in the global std namespace.
+template <typename T>
+class initializer_list {
+  const T* m_begin;
+  decltype(sizeof(0)) m_size;
+
+public:
+  constexpr initializer_list() : m_begin(nullptr), m_size(0) { }
+  constexpr const T* begin() const { return m_begin; }
+  constexpr const T* end() const { return m_begin + m_size; }
+  constexpr decltype(sizeof(0)) size() const { return m_size; }
+};
+
+template <typename T>
+constexpr T min(initializer_list<T> list) {
+  const T* first = list.begin();
+  const T* last = list.end();
+  T result = *first;
+  for (++first; first != last; ++first) {
+    if (*first < result)
+      result = *first;
+  }
+  return result;
+}
+
+} // namespace std
+
+namespace std_initializer_list {
+
+// A braced list passed as std::initializer_list materialises a backing array
+// temporary wrapped in a CXXStdInitializerListExpr. That array is destructed in
+// this function, so it's only safe when its element type destructs trivially.
+
+unsigned [[clang::annotate_type("webkit.nodelete")]] safeSize();
+
+void [[clang::annotate_type("webkit.nodelete")]] callsMinWithInitializerList(unsigned other) {
+  unsigned smallest = std::min({ safeSize(), other, 3u });
+  (void)smallest;
+}
+
+void takesTrackedList(std::initializer_list<ObjectWithNonTrivialDestructor>);
+
+void [[clang::annotate_type("webkit.nodelete")]] passesListOfTrackedObjects() {
+  takesTrackedList({ ObjectWithNonTrivialDestructor(), ObjectWithNonTrivialDestructor() });
+  // expected-warning at -1{{A function 'passesListOfTrackedObjects' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
+}
+
+} // namespace std_initializer_list



More information about the cfe-commits mailing list