[libc-commits] [libc] [libc] Reimplement IN6_IS_ADDR_* macros using statement expressions (PR #209772)

Pavel Labath via libc-commits libc-commits at lists.llvm.org
Wed Jul 15 07:48:23 PDT 2026


https://github.com/labath updated https://github.com/llvm/llvm-project/pull/209772

>From 68497491f58a6db4fe998ab989dbcb6665645ae4 Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Wed, 15 Jul 2026 14:16:03 +0000
Subject: [PATCH 1/2] [libc] Reimplement IN6_IS_ADDR_* macros using statement
 expressions

This patch fixes three issues with the previous implementation:
- the macro argument was being evaluated more than once
- casting to other types (uint32_t in particular) was an aliasing violation
- it was casting to non-const pointers, resulting in compiler errors in cases
  where the user passes a const ptr.

A statement expression fixes the first issue by using a temporary variable. The
second issue is fixed by using the appropriate members of struct in6_addr. The
last issue is fixed by dropping the cast completely. This requires the user to
pass a correctly types pointer (as POSIX requires). Implementations keep it for
compatibility with old code passing void * and similar, but a quick survey shows
that most modern code passes the correct types. If this turns out to be an
issue, we can easily add the cast (to a const type) back.

Implementation notes:
- IN6_IS_ADDR_MULTICAST doesn't use the statement expression because the
  argument is evaluated only once
- I use private helper macros (__IN6_IS_ADDR_UNSPECIFIED and
  __IN6_IS_ADDR_LOOPBACK) so IN6_IS_ADDR_V4COMPAT does not nest statement
  expressions
- I considered using private entrypoints (like we do with cpuset macros) instead
  of statement expressions, but decided against it because these macros are
  simpler and would thus result in a much higher boilerplate-to-useful-code
  ratio (particularly given our restriction on calling other entrypoints)

Assisted by Gemini.
---
 .../llvm-libc-macros/netinet-in-macros.h      |  96 ++++++++++-------
 libc/test/include/netinet_in_test.cpp         | 102 +++++++++---------
 2 files changed, 111 insertions(+), 87 deletions(-)

diff --git a/libc/include/llvm-libc-macros/netinet-in-macros.h b/libc/include/llvm-libc-macros/netinet-in-macros.h
index 9664d512615b9..d7c9d2d2a7e9c 100644
--- a/libc/include/llvm-libc-macros/netinet-in-macros.h
+++ b/libc/include/llvm-libc-macros/netinet-in-macros.h
@@ -11,6 +11,7 @@
 
 #include "../__llvm-libc-common.h"
 #include "../llvm-libc-types/in_addr_t.h"
+#include "../llvm-libc-types/struct_in6_addr.h"
 
 #define IPPROTO_IP 0
 #define IPPROTO_ICMP 1
@@ -42,64 +43,85 @@
 // int and takes a single argument of type const struct in6_addr *:
 // https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/netinet_in.h.html
 
+#define __IN6_IS_ADDR_UNSPECIFIED(a)                                           \
+  ((a)->s6_addr32[0] == 0 && (a)->s6_addr32[1] == 0 &&                         \
+   (a)->s6_addr32[2] == 0 && (a)->s6_addr32[3] == 0)
+
+#define __IN6_IS_ADDR_LOOPBACK(a)                                              \
+  ((a)->s6_addr32[0] == 0 && (a)->s6_addr32[1] == 0 &&                         \
+   (a)->s6_addr32[2] == 0 && (a)->s6_addr[12] == 0 &&                          \
+   (a)->s6_addr[13] == 0 && (a)->s6_addr[14] == 0 && (a)->s6_addr[15] == 1)
+
 #define IN6_IS_ADDR_UNSPECIFIED(a)                                             \
-  ((__LLVM_LIBC_CAST(reinterpret_cast, uint32_t *, a)[0]) == 0 &&              \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint32_t *, a)[1]) == 0 &&              \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint32_t *, a)[2]) == 0 &&              \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint32_t *, a)[3]) == 0)
+  (__extension__({                                                             \
+    const struct in6_addr *__a = (a);                                          \
+    __IN6_IS_ADDR_UNSPECIFIED(__a);                                            \
+  }))
 
 #define IN6_IS_ADDR_LOOPBACK(a)                                                \
-  ((__LLVM_LIBC_CAST(reinterpret_cast, uint32_t *, a)[0]) == 0 &&              \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint32_t *, a)[1]) == 0 &&              \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint32_t *, a)[2]) == 0 &&              \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint8_t *, a)[12]) == 0 &&              \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint8_t *, a)[13]) == 0 &&              \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint8_t *, a)[14]) == 0 &&              \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint8_t *, a)[15]) == 1)
+  (__extension__({                                                             \
+    const struct in6_addr *__a = (a);                                          \
+    __IN6_IS_ADDR_LOOPBACK(__a);                                               \
+  }))
 
-#define IN6_IS_ADDR_MULTICAST(a)                                               \
-  (__LLVM_LIBC_CAST(reinterpret_cast, uint8_t *, a)[0]) == 0xff
+#define IN6_IS_ADDR_MULTICAST(a) ((a)->s6_addr[0] == 0xff)
 
 #define IN6_IS_ADDR_LINKLOCAL(a)                                               \
-  ((__LLVM_LIBC_CAST(reinterpret_cast, uint8_t *, a)[0]) == 0xfe &&            \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint8_t *, a)[1] & 0xc0) == 0x80)
+  (__extension__({                                                             \
+    const struct in6_addr *__a = (a);                                          \
+    __a->s6_addr[0] == 0xfe && (__a->s6_addr[1] & 0xc0) == 0x80;               \
+  }))
 
 #define IN6_IS_ADDR_SITELOCAL(a)                                               \
-  ((__LLVM_LIBC_CAST(reinterpret_cast, uint8_t *, a)[0]) == 0xfe &&            \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint8_t *, a)[1] & 0xc0) == 0xc0)
+  (__extension__({                                                             \
+    const struct in6_addr *__a = (a);                                          \
+    __a->s6_addr[0] == 0xfe && (__a->s6_addr[1] & 0xc0) == 0xc0;               \
+  }))
 
 #define IN6_IS_ADDR_V4MAPPED(a)                                                \
-  ((__LLVM_LIBC_CAST(reinterpret_cast, uint32_t *, a)[0]) == 0 &&              \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint32_t *, a)[1]) == 0 &&              \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint8_t *, a)[8]) == 0 &&               \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint8_t *, a)[9]) == 0 &&               \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint8_t *, a)[10]) == 0xff &&           \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint8_t *, a)[11]) == 0xff)
+  (__extension__({                                                             \
+    const struct in6_addr *__a = (a);                                          \
+    __a->s6_addr32[0] == 0 && __a->s6_addr32[1] == 0 &&                        \
+        __a->s6_addr[8] == 0 && __a->s6_addr[9] == 0 &&                        \
+        __a->s6_addr[10] == 0xff && __a->s6_addr[11] == 0xff;                  \
+  }))
 
 #define IN6_IS_ADDR_V4COMPAT(a)                                                \
-  ((__LLVM_LIBC_CAST(reinterpret_cast, uint32_t *, a)[0]) == 0 &&              \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint32_t *, a)[1]) == 0 &&              \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint32_t *, a)[2]) == 0 &&              \
-   !IN6_IS_ADDR_UNSPECIFIED(a) && !IN6_IS_ADDR_LOOPBACK(a))
+  (__extension__({                                                             \
+    const struct in6_addr *__a = (a);                                          \
+    __a->s6_addr32[0] == 0 && __a->s6_addr32[1] == 0 &&                        \
+        __a->s6_addr32[2] == 0 && !__IN6_IS_ADDR_UNSPECIFIED(__a) &&           \
+        !__IN6_IS_ADDR_LOOPBACK(__a);                                          \
+  }))
 
 #define IN6_IS_ADDR_MC_NODELOCAL(a)                                            \
-  (IN6_IS_ADDR_MULTICAST(a) &&                                                 \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint8_t *, a)[1] & 0xf) == 0x1)
+  (__extension__({                                                             \
+    const struct in6_addr *__a = (a);                                          \
+    IN6_IS_ADDR_MULTICAST(__a) && (__a->s6_addr[1] & 0xf) == 0x1;              \
+  }))
 
 #define IN6_IS_ADDR_MC_LINKLOCAL(a)                                            \
-  (IN6_IS_ADDR_MULTICAST(a) &&                                                 \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint8_t *, a)[1] & 0xf) == 0x2)
+  (__extension__({                                                             \
+    const struct in6_addr *__a = (a);                                          \
+    IN6_IS_ADDR_MULTICAST(__a) && (__a->s6_addr[1] & 0xf) == 0x2;              \
+  }))
 
 #define IN6_IS_ADDR_MC_SITELOCAL(a)                                            \
-  (IN6_IS_ADDR_MULTICAST(a) &&                                                 \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint8_t *, a)[1] & 0xf) == 0x5)
+  (__extension__({                                                             \
+    const struct in6_addr *__a = (a);                                          \
+    IN6_IS_ADDR_MULTICAST(__a) && (__a->s6_addr[1] & 0xf) == 0x5;              \
+  }))
 
 #define IN6_IS_ADDR_MC_ORGLOCAL(a)                                             \
-  (IN6_IS_ADDR_MULTICAST(a) &&                                                 \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint8_t *, a)[1] & 0xf) == 0x8)
+  (__extension__({                                                             \
+    const struct in6_addr *__a = (a);                                          \
+    IN6_IS_ADDR_MULTICAST(__a) && (__a->s6_addr[1] & 0xf) == 0x8;              \
+  }))
 
 #define IN6_IS_ADDR_MC_GLOBAL(a)                                               \
-  (IN6_IS_ADDR_MULTICAST(a) &&                                                 \
-   (__LLVM_LIBC_CAST(reinterpret_cast, uint8_t *, a)[1] & 0xf) == 0xe)
+  (__extension__({                                                             \
+    const struct in6_addr *__a = (a);                                          \
+    IN6_IS_ADDR_MULTICAST(__a) && (__a->s6_addr[1] & 0xf) == 0xe;              \
+  }))
 
 #endif // LLVM_LIBC_MACROS_NETINET_IN_MACROS_H
diff --git a/libc/test/include/netinet_in_test.cpp b/libc/test/include/netinet_in_test.cpp
index 2fb46a9227c07..03f4c052ab9b2 100644
--- a/libc/test/include/netinet_in_test.cpp
+++ b/libc/test/include/netinet_in_test.cpp
@@ -10,68 +10,70 @@
 #include "test/UnitTest/Test.h"
 
 TEST(LlvmLibcNetinetInTest, IN6Macro) {
-  char buff[16] = {};
+  struct in6_addr addr = {};
+  const struct in6_addr *const_addr = &addr;
 
-  EXPECT_TRUE(IN6_IS_ADDR_UNSPECIFIED(buff));
+  EXPECT_TRUE(IN6_IS_ADDR_UNSPECIFIED(&addr));
+  EXPECT_TRUE(IN6_IS_ADDR_UNSPECIFIED(const_addr));
   for (int i = 0; i < 16; ++i) {
-    buff[i] = 1;
-    EXPECT_FALSE(IN6_IS_ADDR_UNSPECIFIED(buff));
-    buff[i] = 0;
+    addr.s6_addr[i] = 1;
+    EXPECT_FALSE(IN6_IS_ADDR_UNSPECIFIED(&addr));
+    addr.s6_addr[i] = 0;
   }
 
-  EXPECT_FALSE(IN6_IS_ADDR_LOOPBACK(buff));
-  buff[15] = 1;
-  EXPECT_TRUE(IN6_IS_ADDR_LOOPBACK(buff));
-  buff[15] = 0;
+  EXPECT_FALSE(IN6_IS_ADDR_LOOPBACK(&addr));
+  addr.s6_addr[15] = 1;
+  EXPECT_TRUE(IN6_IS_ADDR_LOOPBACK(&addr));
+  addr.s6_addr[15] = 0;
 
-  EXPECT_FALSE(IN6_IS_ADDR_MULTICAST(buff));
-  buff[0] = 0xff;
-  EXPECT_TRUE(IN6_IS_ADDR_MULTICAST(buff));
-  buff[0] = 0;
+  EXPECT_FALSE(IN6_IS_ADDR_MULTICAST(&addr));
+  addr.s6_addr[0] = 0xff;
+  EXPECT_TRUE(IN6_IS_ADDR_MULTICAST(&addr));
+  addr.s6_addr[0] = 0;
 
-  buff[0] = 0xfe;
-  buff[1] = 0x80;
-  EXPECT_TRUE(IN6_IS_ADDR_LINKLOCAL(buff));
-  buff[0] = 0xff;
-  buff[1] = 0x80;
-  EXPECT_FALSE(IN6_IS_ADDR_LINKLOCAL(buff));
+  addr.s6_addr[0] = 0xfe;
+  addr.s6_addr[1] = 0x80;
+  EXPECT_TRUE(IN6_IS_ADDR_LINKLOCAL(&addr));
+  addr.s6_addr[0] = 0xff;
+  addr.s6_addr[1] = 0x80;
+  EXPECT_FALSE(IN6_IS_ADDR_LINKLOCAL(&addr));
 
-  buff[0] = 0xfe;
-  buff[1] = 0xc0;
-  EXPECT_TRUE(IN6_IS_ADDR_SITELOCAL(buff));
-  buff[0] = 0xff;
-  buff[1] = 0x80;
-  EXPECT_FALSE(IN6_IS_ADDR_SITELOCAL(buff));
+  addr.s6_addr[0] = 0xfe;
+  addr.s6_addr[1] = 0xc0;
+  EXPECT_TRUE(IN6_IS_ADDR_SITELOCAL(&addr));
+  addr.s6_addr[0] = 0xff;
+  addr.s6_addr[1] = 0x80;
+  EXPECT_FALSE(IN6_IS_ADDR_SITELOCAL(&addr));
 
-  buff[0] = 0xff;
-  buff[1] = 0x1;
-  EXPECT_TRUE(IN6_IS_ADDR_MC_NODELOCAL(buff));
-  buff[1] = 0x2;
-  EXPECT_TRUE(IN6_IS_ADDR_MC_LINKLOCAL(buff));
-  buff[1] = 0x5;
-  EXPECT_TRUE(IN6_IS_ADDR_MC_SITELOCAL(buff));
-  buff[1] = 0x8;
-  EXPECT_TRUE(IN6_IS_ADDR_MC_ORGLOCAL(buff));
-  buff[1] = 0xe;
-  EXPECT_TRUE(IN6_IS_ADDR_MC_GLOBAL(buff));
-  buff[1] = 0;
-  buff[0] = 0;
+  addr.s6_addr[0] = 0xff;
+  addr.s6_addr[1] = 0x1;
+  EXPECT_TRUE(IN6_IS_ADDR_MC_NODELOCAL(&addr));
+  addr.s6_addr[1] = 0x2;
+  EXPECT_TRUE(IN6_IS_ADDR_MC_LINKLOCAL(&addr));
+  addr.s6_addr[1] = 0x5;
+  EXPECT_TRUE(IN6_IS_ADDR_MC_SITELOCAL(&addr));
+  addr.s6_addr[1] = 0x8;
+  EXPECT_TRUE(IN6_IS_ADDR_MC_ORGLOCAL(&addr));
+  addr.s6_addr[1] = 0xe;
+  EXPECT_TRUE(IN6_IS_ADDR_MC_GLOBAL(&addr));
+  addr.s6_addr[1] = 0;
+  addr.s6_addr[0] = 0;
 
-  EXPECT_FALSE(IN6_IS_ADDR_V4MAPPED(buff));
-  buff[10] = 0xff;
-  buff[11] = 0xff;
-  EXPECT_TRUE(IN6_IS_ADDR_V4MAPPED(buff));
-  buff[10] = 0;
-  buff[11] = 0;
+  EXPECT_FALSE(IN6_IS_ADDR_V4MAPPED(&addr));
+  addr.s6_addr[10] = 0xff;
+  addr.s6_addr[11] = 0xff;
+  EXPECT_TRUE(IN6_IS_ADDR_V4MAPPED(&addr));
+  addr.s6_addr[10] = 0;
+  addr.s6_addr[11] = 0;
 
   for (int i = 12; i < 16; ++i) {
-    buff[i] ^= 42;
-    EXPECT_TRUE(IN6_IS_ADDR_V4COMPAT(buff));
-    buff[i] ^= 42;
+    addr.s6_addr[i] ^= 42;
+    EXPECT_TRUE(IN6_IS_ADDR_V4COMPAT(&addr));
+    addr.s6_addr[i] ^= 42;
   }
   for (int i = 0; i < 12; ++i) {
-    buff[i] ^= 42;
-    EXPECT_FALSE(IN6_IS_ADDR_V4COMPAT(buff));
-    buff[i] ^= 42;
+    addr.s6_addr[i] ^= 42;
+    EXPECT_FALSE(IN6_IS_ADDR_V4COMPAT(&addr));
+    addr.s6_addr[i] ^= 42;
   }
 }

>From 9f1b3f9149d8a1d64c45978e11a6664799d155e6 Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Wed, 15 Jul 2026 14:48:04 +0000
Subject: [PATCH 2/2] format

---
 libc/include/llvm-libc-macros/netinet-in-macros.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libc/include/llvm-libc-macros/netinet-in-macros.h b/libc/include/llvm-libc-macros/netinet-in-macros.h
index d7c9d2d2a7e9c..1636736d3b26d 100644
--- a/libc/include/llvm-libc-macros/netinet-in-macros.h
+++ b/libc/include/llvm-libc-macros/netinet-in-macros.h
@@ -49,8 +49,8 @@
 
 #define __IN6_IS_ADDR_LOOPBACK(a)                                              \
   ((a)->s6_addr32[0] == 0 && (a)->s6_addr32[1] == 0 &&                         \
-   (a)->s6_addr32[2] == 0 && (a)->s6_addr[12] == 0 &&                          \
-   (a)->s6_addr[13] == 0 && (a)->s6_addr[14] == 0 && (a)->s6_addr[15] == 1)
+   (a)->s6_addr32[2] == 0 && (a)->s6_addr[12] == 0 && (a)->s6_addr[13] == 0 && \
+   (a)->s6_addr[14] == 0 && (a)->s6_addr[15] == 1)
 
 #define IN6_IS_ADDR_UNSPECIFIED(a)                                             \
   (__extension__({                                                             \



More information about the libc-commits mailing list