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);