Commit d7e4fc76ad5 for php.net

commit d7e4fc76ad50365a5299632d515f387f8fbf7ffa
Author: Tim Düsterhus <tim@bastelstu.be>
Date:   Sun Jun 14 11:34:21 2026 +0200

    zend_types: Move `zend_gc_*()` functions above the wrapper macros (#21956)

    That allows reliable usage of the `GC_*()` macros in (inline) functions in
    zend_types.h.

diff --git a/Zend/zend_types.h b/Zend/zend_types.h
index cfff3b942c4..f3039d20251 100644
--- a/Zend/zend_types.h
+++ b/Zend/zend_types.h
@@ -713,6 +713,116 @@ static zend_always_inline uint8_t zval_get_type(const zval* pz) {
 #define Z_TYPE_FLAGS_SHIFT			8
 #define Z_TYPE_INFO_EXTRA_SHIFT		16

+/* zval_gc_flags(zval.value->gc.u.type_info) (common flags) */
+#define GC_NOT_COLLECTABLE			(1<<4)
+#define GC_PROTECTED                (1<<5) /* used for recursion detection */
+#define GC_IMMUTABLE                (1<<6) /* can't be changed in place */
+#define GC_PERSISTENT               (1<<7) /* allocated using malloc */
+#define GC_PERSISTENT_LOCAL         (1<<8) /* persistent, but thread-local */
+
+#define GC_TYPE_MASK				0x0000000f
+#define GC_FLAGS_MASK				0x000003f0
+#define GC_INFO_MASK				0xfffffc00
+#define GC_FLAGS_SHIFT				0
+#define GC_INFO_SHIFT				10
+
+static zend_always_inline uint8_t zval_gc_type(uint32_t gc_type_info) {
+	return (gc_type_info & GC_TYPE_MASK);
+}
+
+static zend_always_inline uint32_t zval_gc_flags(uint32_t gc_type_info) {
+	return (gc_type_info >> GC_FLAGS_SHIFT) & (GC_FLAGS_MASK >> GC_FLAGS_SHIFT);
+}
+
+static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) {
+	return (gc_type_info >> GC_INFO_SHIFT);
+}
+
+#define GC_TYPE_INFO(p)				(p)->gc.u.type_info
+#define GC_TYPE(p)					zval_gc_type(GC_TYPE_INFO(p))
+#define GC_FLAGS(p)					zval_gc_flags(GC_TYPE_INFO(p))
+#define GC_INFO(p)					zval_gc_info(GC_TYPE_INFO(p))
+
+#define GC_ADD_FLAGS(p, flags) do { \
+		GC_TYPE_INFO(p) |= (flags) << GC_FLAGS_SHIFT; \
+	} while (0)
+#define GC_DEL_FLAGS(p, flags) do { \
+		GC_TYPE_INFO(p) &= ~((flags) << GC_FLAGS_SHIFT); \
+	} while (0)
+
+#ifndef ZEND_RC_DEBUG
+# define ZEND_RC_DEBUG 0
+#endif
+
+#if ZEND_RC_DEBUG
+extern ZEND_API bool zend_rc_debug;
+/* The GC_PERSISTENT flag is reused for IS_OBJ_WEAKLY_REFERENCED on objects.
+ * Skip checks for OBJECT/NULL type to avoid interpreting the flag incorrectly. */
+# define ZEND_RC_MOD_CHECK(p) do { \
+		if (zend_rc_debug) { \
+			uint8_t type = zval_gc_type((p)->u.type_info); \
+			if (type != IS_OBJECT && type != IS_NULL) { \
+				ZEND_ASSERT(!(zval_gc_flags((p)->u.type_info) & GC_IMMUTABLE)); \
+				ZEND_ASSERT((zval_gc_flags((p)->u.type_info) & (GC_PERSISTENT|GC_PERSISTENT_LOCAL)) != GC_PERSISTENT); \
+			} \
+		} \
+	} while (0)
+# define GC_MAKE_PERSISTENT_LOCAL(p) do { \
+		GC_ADD_FLAGS(p, GC_PERSISTENT_LOCAL); \
+	} while (0)
+#else
+# define ZEND_RC_MOD_CHECK(p) \
+	do { } while (0)
+# define GC_MAKE_PERSISTENT_LOCAL(p) \
+	do { } while (0)
+#endif
+
+static zend_always_inline uint32_t zend_gc_refcount(const zend_refcounted_h *p) {
+	return p->refcount;
+}
+
+static zend_always_inline uint32_t zend_gc_set_refcount(zend_refcounted_h *p, uint32_t rc) {
+	p->refcount = rc;
+	return p->refcount;
+}
+
+static zend_always_inline uint32_t zend_gc_addref(zend_refcounted_h *p) {
+	ZEND_RC_MOD_CHECK(p);
+	return ++(p->refcount);
+}
+
+static zend_always_inline void zend_gc_try_addref(zend_refcounted_h *p) {
+	if (!(p->u.type_info & GC_IMMUTABLE)) {
+		ZEND_RC_MOD_CHECK(p);
+		++p->refcount;
+	}
+}
+
+static zend_always_inline void zend_gc_try_delref(zend_refcounted_h *p) {
+	if (!(p->u.type_info & GC_IMMUTABLE)) {
+		ZEND_RC_MOD_CHECK(p);
+		--p->refcount;
+	}
+}
+
+static zend_always_inline uint32_t zend_gc_delref(zend_refcounted_h *p) {
+	ZEND_ASSERT(p->refcount > 0);
+	ZEND_RC_MOD_CHECK(p);
+	return --(p->refcount);
+}
+
+static zend_always_inline uint32_t zend_gc_addref_ex(zend_refcounted_h *p, uint32_t rc) {
+	ZEND_RC_MOD_CHECK(p);
+	p->refcount += rc;
+	return p->refcount;
+}
+
+static zend_always_inline uint32_t zend_gc_delref_ex(zend_refcounted_h *p, uint32_t rc) {
+	ZEND_RC_MOD_CHECK(p);
+	p->refcount -= rc;
+	return p->refcount;
+}
+
 #define GC_REFCOUNT(p)				zend_gc_refcount(&(p)->gc)
 #define GC_SET_REFCOUNT(p, rc)		zend_gc_set_refcount(&(p)->gc, rc)
 #define GC_ADDREF(p)				zend_gc_addref(&(p)->gc)
@@ -754,36 +864,6 @@ static zend_always_inline uint8_t zval_get_type(const zval* pz) {
 		} \
 	} while (0)

-#define GC_TYPE_MASK				0x0000000f
-#define GC_FLAGS_MASK				0x000003f0
-#define GC_INFO_MASK				0xfffffc00
-#define GC_FLAGS_SHIFT				0
-#define GC_INFO_SHIFT				10
-
-static zend_always_inline uint8_t zval_gc_type(uint32_t gc_type_info) {
-	return (gc_type_info & GC_TYPE_MASK);
-}
-
-static zend_always_inline uint32_t zval_gc_flags(uint32_t gc_type_info) {
-	return (gc_type_info >> GC_FLAGS_SHIFT) & (GC_FLAGS_MASK >> GC_FLAGS_SHIFT);
-}
-
-static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) {
-	return (gc_type_info >> GC_INFO_SHIFT);
-}
-
-#define GC_TYPE_INFO(p)				(p)->gc.u.type_info
-#define GC_TYPE(p)					zval_gc_type(GC_TYPE_INFO(p))
-#define GC_FLAGS(p)					zval_gc_flags(GC_TYPE_INFO(p))
-#define GC_INFO(p)					zval_gc_info(GC_TYPE_INFO(p))
-
-#define GC_ADD_FLAGS(p, flags) do { \
-		GC_TYPE_INFO(p) |= (flags) << GC_FLAGS_SHIFT; \
-	} while (0)
-#define GC_DEL_FLAGS(p, flags) do { \
-		GC_TYPE_INFO(p) &= ~((flags) << GC_FLAGS_SHIFT); \
-	} while (0)
-
 #define Z_GC_TYPE(zval)				GC_TYPE(Z_COUNTED(zval))
 #define Z_GC_TYPE_P(zval_p)			Z_GC_TYPE(*(zval_p))

@@ -795,13 +875,6 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) {
 #define Z_GC_TYPE_INFO(zval)		GC_TYPE_INFO(Z_COUNTED(zval))
 #define Z_GC_TYPE_INFO_P(zval_p)	Z_GC_TYPE_INFO(*(zval_p))

-/* zval_gc_flags(zval.value->gc.u.type_info) (common flags) */
-#define GC_NOT_COLLECTABLE			(1<<4)
-#define GC_PROTECTED                (1<<5) /* used for recursion detection */
-#define GC_IMMUTABLE                (1<<6) /* can't be changed in place */
-#define GC_PERSISTENT               (1<<7) /* allocated using malloc */
-#define GC_PERSISTENT_LOCAL         (1<<8) /* persistent, but thread-local */
-
 #define GC_NULL						(IS_NULL         | (GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT))
 #define GC_STRING					(IS_STRING       | (GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT))
 #define GC_ARRAY					IS_ARRAY
@@ -1299,79 +1372,6 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) {
 #define Z_TRY_ADDREF(z)				Z_TRY_ADDREF_P(&(z))
 #define Z_TRY_DELREF(z)				Z_TRY_DELREF_P(&(z))

-#ifndef ZEND_RC_DEBUG
-# define ZEND_RC_DEBUG 0
-#endif
-
-#if ZEND_RC_DEBUG
-extern ZEND_API bool zend_rc_debug;
-/* The GC_PERSISTENT flag is reused for IS_OBJ_WEAKLY_REFERENCED on objects.
- * Skip checks for OBJECT/NULL type to avoid interpreting the flag incorrectly. */
-# define ZEND_RC_MOD_CHECK(p) do { \
-		if (zend_rc_debug) { \
-			uint8_t type = zval_gc_type((p)->u.type_info); \
-			if (type != IS_OBJECT && type != IS_NULL) { \
-				ZEND_ASSERT(!(zval_gc_flags((p)->u.type_info) & GC_IMMUTABLE)); \
-				ZEND_ASSERT((zval_gc_flags((p)->u.type_info) & (GC_PERSISTENT|GC_PERSISTENT_LOCAL)) != GC_PERSISTENT); \
-			} \
-		} \
-	} while (0)
-# define GC_MAKE_PERSISTENT_LOCAL(p) do { \
-		GC_ADD_FLAGS(p, GC_PERSISTENT_LOCAL); \
-	} while (0)
-#else
-# define ZEND_RC_MOD_CHECK(p) \
-	do { } while (0)
-# define GC_MAKE_PERSISTENT_LOCAL(p) \
-	do { } while (0)
-#endif
-
-static zend_always_inline uint32_t zend_gc_refcount(const zend_refcounted_h *p) {
-	return p->refcount;
-}
-
-static zend_always_inline uint32_t zend_gc_set_refcount(zend_refcounted_h *p, uint32_t rc) {
-	p->refcount = rc;
-	return p->refcount;
-}
-
-static zend_always_inline uint32_t zend_gc_addref(zend_refcounted_h *p) {
-	ZEND_RC_MOD_CHECK(p);
-	return ++(p->refcount);
-}
-
-static zend_always_inline void zend_gc_try_addref(zend_refcounted_h *p) {
-	if (!(p->u.type_info & GC_IMMUTABLE)) {
-		ZEND_RC_MOD_CHECK(p);
-		++p->refcount;
-	}
-}
-
-static zend_always_inline void zend_gc_try_delref(zend_refcounted_h *p) {
-	if (!(p->u.type_info & GC_IMMUTABLE)) {
-		ZEND_RC_MOD_CHECK(p);
-		--p->refcount;
-	}
-}
-
-static zend_always_inline uint32_t zend_gc_delref(zend_refcounted_h *p) {
-	ZEND_ASSERT(p->refcount > 0);
-	ZEND_RC_MOD_CHECK(p);
-	return --(p->refcount);
-}
-
-static zend_always_inline uint32_t zend_gc_addref_ex(zend_refcounted_h *p, uint32_t rc) {
-	ZEND_RC_MOD_CHECK(p);
-	p->refcount += rc;
-	return p->refcount;
-}
-
-static zend_always_inline uint32_t zend_gc_delref_ex(zend_refcounted_h *p, uint32_t rc) {
-	ZEND_RC_MOD_CHECK(p);
-	p->refcount -= rc;
-	return p->refcount;
-}
-
 static zend_always_inline uint32_t zval_refcount_p(const zval* pz) {
 #if ZEND_DEBUG
 	ZEND_ASSERT(Z_REFCOUNTED_P(pz) || Z_TYPE_P(pz) == IS_ARRAY);