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
