https://github.com/rjodinchr updated 
https://github.com/llvm/llvm-project/pull/196571

>From 97d98f922be0f3ccf1fec99daef82cf40992531e Mon Sep 17 00:00:00 2001
From: Romaric Jodin <[email protected]>
Date: Fri, 8 May 2026 16:14:06 +0200
Subject: [PATCH] Fix MSVC template parsing error in SerializationFormat

This commit fixes a hard compilation error on Windows (when building with
Clang's MSVC compatibility mode) and a subsequent access violation that
occurred during Windows CI testing.

Root Causes:
1. When compiling with `-fms-compatibility`, Clang's two-phase template
   lookup fails to resolve function-local static variables (`SavedSerialize`
   and `SavedDeserialize`) captured by a local class (`ConcreteCodec`) inside
   an uninstantiated template. It incorrectly assumes they are members of a
   dependent base class.
2. Originally, `TypedSerializerFn` and `DeserializerFn` were typed as
   `llvm::function_ref`. Storing these in static variables created dangling
   pointers, as `function_ref` is a non-owning wrapper that only referenced
   the temporaries decaying on the constructor's stack, causing an 0xC0000005
   access violation on x64 Windows.

The Fix:
* Hoisted `SavedSerialize` and `SavedDeserialize` out of the constructor
  scope to be `static inline` members of the `Add` class template. This allows
  Clang's Phase 1 parser to correctly resolve the symbols.
* Redefined `TypedSerializerFn` and `DeserializerFn` to raw function pointers
  instead of `llvm::function_ref`. This ensures the static registry variables
  safely capture the persistent addresses of the global functions being
  registered, eliminating the dangling pointers.

Safety note: The original concern regarding `dlopen` symbol visibility on
Linux is preserved. `ConcreteCodec` continues to store the functions strictly
as instance members, snapshotting the plugin's local copy of the static
variables at instantiation.
---
 .../Core/Serialization/SerializationFormat.h  | 30 +++++++++++--------
 1 file changed, 18 insertions(+), 12 deletions(-)

diff --git 
a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Serialization/SerializationFormat.h
 
b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Serialization/SerializationFormat.h
index fd261c6d9a723..080628a700ba1 100644
--- 
a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Serialization/SerializationFormat.h
+++ 
b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Serialization/SerializationFormat.h
@@ -111,7 +111,7 @@ class SerializationFormat {
       FormatT, llvm::function_ref<SerRet(const AnalysisResult &, SerArgs...)>,
       llvm::function_ref<DesRet(DesArgs...)>> {
 
-    using DeserializerFn = llvm::function_ref<DesRet(DesArgs...)>;
+    using DeserializerFn = DesRet (*)(DesArgs...);
 
   public:
     /// Abstract base type stored in \c llvm::Registry<Codec>.
@@ -130,8 +130,10 @@ class SerializationFormat {
     };
 
     template <class AnalysisResultT> struct Add {
-      using TypedSerializerFn =
-          llvm::function_ref<SerRet(const AnalysisResultT &, SerArgs...)>;
+      using TypedSerializerFn = SerRet (*)(const AnalysisResultT &, 
SerArgs...);
+
+      static inline TypedSerializerFn SavedSerialize;
+      static inline DeserializerFn SavedDeserialize;
 
       /// Takes the plugin's typed serializer and the deserializer, and
       /// inserts them into \c llvm::Registry<Codec>.
@@ -147,15 +149,19 @@ class SerializationFormat {
         Registered = true;
 
         /// The plugin's serializer and deserializer are captured in
-        /// function-local statics so that the \c ConcreteCodec default
-        /// constructor (required by \c llvm::Registry) can read them.
-        /// They are stored as instance members of \c ConcreteCodec rather
-        /// than \c static \c inline class members to avoid symbol
-        /// visibility issues across shared library boundaries on Linux
-        /// (where \c dlopen with \c RTLD_LOCAL can give the host and
-        /// plugin separate copies of \c static \c inline members).
-        static TypedSerializerFn SavedSerialize = TypedSerialize;
-        static DeserializerFn SavedDeserialize = Deserialize;
+        /// static inline members of the Add template so that the
+        /// \c ConcreteCodec default constructor (required by \c 
llvm::Registry)
+        /// can read them. They use raw function pointers to prevent dangling
+        /// references to temporary stack variables during registration.
+        ///
+        /// Once read by the constructor, they are stored as instance members
+        /// of \c ConcreteCodec rather than directly executed from the \c 
static
+        /// \c inline class members. This prevents symbol visibility issues
+        /// across shared library boundaries on Linux (where \c dlopen with \c
+        /// RTLD_LOCAL can give the host and plugin separate copies of \c 
static
+        /// \c inline members).
+        SavedSerialize = TypedSerialize;
+        SavedDeserialize = Deserialize;
 
         /// Concrete subclass of \c Codec for \c AnalysisResultT.
         /// The \c serialize() override performs the downcast from

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to