Dune 3.15.3 + OCaml 5.2 data race = false positive?

Looks like GCC has recently learned about has_feature, so this ‘if’ is the wrong way around, probably the GCC version should be first:

#define CAMLno_tsan
/* __has_feature is Clang-specific, but GCC defines __SANITIZE_ADDRESS__ and
 * __SANITIZE_THREAD__. */
#if defined(__has_feature)
#  if __has_feature(thread_sanitizer)
#    undef CAMLno_tsan
#    if defined(__has_attribute)
#      if __has_attribute(disable_sanitizer_instrumentation)
#        define CAMLno_tsan \
           __attribute__((disable_sanitizer_instrumentation))
#      else
#        define CAMLno_tsan __attribute__((no_sanitize("thread")))
#      endif
#    else
#      define CAMLno_tsan __attribute__((no_sanitize("thread")))
#    endif
#  endif
#else
#  if defined(__SANITIZE_THREAD__)
#    undef CAMLno_tsan
#    define CAMLno_tsan __attribute__((no_sanitize_thread))
#  endif
#endif

This patch seems to fix the problem, and at least on some quick testing with GCC-14 and clang-18 it seems to turn TSAN on/off correctly for a function, but I’ll need to do more testing before I open a PR (e.g. figure a way to add a test to the testsuite run in the CI that checks whether the attribute works or not. I can determine that manually by inspecting the assembly, but for the testsuite it probably needs a small test that triggers a race on purpose and checks that it is correctly present/absent based on this flag):

diff --git a/runtime/caml/misc.h b/runtime/caml/misc.h
index ddccdc585d..b373c7f525 100644
--- a/runtime/caml/misc.h
+++ b/runtime/caml/misc.h
@@ -562,18 +562,20 @@ CAMLextern int caml_snwprintf(wchar_t * buf,
 
 /* Macro used to deactivate address sanitizer on some functions. */
 #define CAMLno_asan
-/* __has_feature is Clang-specific, but GCC defines __SANITIZE_ADDRESS__ and
- * __SANITIZE_THREAD__. */
-#if defined(__has_feature)
+#if defined(__SANITIZE_ADDRESS__)
+#    undef CAMLno_asan
+#    define CAMLno_asan __attribute__((no_sanitize_address))
+#elif defined(__has_feature)
+/* __has_feature used to be Clang-specific, and GCC defines __SANITIZE_ADDRESS__ and
+ * __SANITIZE_THREAD__ instead.
+ * Newer versions of GCC support __has_feature, but do not support all the attributes
+ * the Clang does, so we must check for GCC first, because `no_sanitize("thread")`
+ * doesn't work on GCC.
+ */
 #  if __has_feature(address_sanitizer)
 #    undef CAMLno_asan
 #    define CAMLno_asan __attribute__((no_sanitize("address")))
 #  endif
-#else
-#  if defined(__SANITIZE_ADDRESS__)
-#    undef CAMLno_asan
-#    define CAMLno_asan __attribute__((no_sanitize_address))
-#  endif
 #endif
 
 #endif /* CAML_INTERNALS */
diff --git a/runtime/caml/tsan.h b/runtime/caml/tsan.h
index 2f1df2f4fa..38e0c1dabd 100644
--- a/runtime/caml/tsan.h
+++ b/runtime/caml/tsan.h
@@ -17,9 +17,16 @@
 
 /* Macro used to deactivate thread sanitizer on some functions. */
 #define CAMLno_tsan
-/* __has_feature is Clang-specific, but GCC defines __SANITIZE_ADDRESS__ and
- * __SANITIZE_THREAD__. */
-#if defined(__has_feature)
+#if defined(__SANITIZE_THREAD__)
+#    undef CAMLno_tsan
+#    define CAMLno_tsan __attribute__((no_sanitize_thread))
+#elif defined(__has_feature)
+/* __has_feature used to be Clang-specific, and GCC defines __SANITIZE_ADDRESS__ and
+ * __SANITIZE_THREAD__ instead.
+ * Newer versions of GCC support __has_feature, but do not support all the attributes
+ * the Clang does, so we must check for GCC first, because `no_sanitize("thread")`
+ * doesn't work on GCC.
+ */
 #  if __has_feature(thread_sanitizer)
 #    undef CAMLno_tsan
 #    if defined(__has_attribute)
@@ -33,11 +40,6 @@
 #      define CAMLno_tsan __attribute__((no_sanitize("thread")))
 #    endif
 #  endif
-#else
-#  if defined(__SANITIZE_THREAD__)
-#    undef CAMLno_tsan
-#    define CAMLno_tsan __attribute__((no_sanitize_thread))
-#  endif
 #endif
 
 /* TSan records a release operation on encountering ANNOTATE_HAPPENS_BEFORE