[clang] [clang] Fix acceptance of redefinition of a struct with different bit-field width (PR #235265)

Ivy Xu via cfe-commits cfe-commits at lists.llvm.org
Sat Oct 24 01:57:34 PDT 2026


https://github.com/IvyXu420 created https://github.com/llvm/llvm-project/pull/235265

The bug comes from `IsStructurallyEquivalent(Context, Field1->getBitWidth(), Field2->getBitWidth())`
returning true when both `Field1->getBitWidth()` and `Field2->getBitWidth()` are ConstantExpr types. Two bit fields were treated as structurally equivalent even though their widths are different.

This PR makes sure that two bit-fields with different bit-field width are treated as structurally unequivalent under `-std=c23`.

Fixes #232748.



>From 8c565c58747c72691a15110575d147a27b84fb90 Mon Sep 17 00:00:00 2001
From: Ivy Xu <ivyxu1337 at gmail.com>
Date: Sat, 24 Oct 2026 15:40:04 +0800
Subject: [PATCH] Fix `IsStructurallyEquivalent` for constexpr BitField

---
 clang/lib/AST/ASTStructuralEquivalence.cpp | 11 ++++++-----
 clang/test/C/C23/n3037.c                   | 13 +++++++++++++
 2 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index 029e33ae0db760..f6fa067810550f 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -1664,13 +1664,13 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
     return false;
   }
 
-  if ((Field1->isBitField() || Field2->isBitField()) &&
-      !IsStructurallyEquivalent(Context, Field1->getBitWidth(),
-                                Field2->getBitWidth())) {
+  if ((Field1->isBitField() || Field2->isBitField())) {
     // Two bit-fields can be structurally unequivalent but still be okay for
     // the purposes of C where they simply need to have the same values, not
     // the same token sequences.
-    bool Diagnose = true;
+    bool Diagnose = false;
+    if (!IsStructurallyEquivalent(Context, Field1->getBitWidth(), Field2->getBitWidth()))
+        Diagnose = true;
     if (Context.LangOpts.C23 && Field1->isBitField() && Field2->isBitField())
       Diagnose = Field1->getBitWidthValue() != Field2->getBitWidthValue();
 
@@ -1695,7 +1695,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
       DiagNote(Field2, &StructuralEquivalenceContext::Diag2);
       DiagNote(Field1, &StructuralEquivalenceContext::Diag1);
     }
-    return false;
+    if (Diagnose)
+      return false;
   }
 
   return true;
diff --git a/clang/test/C/C23/n3037.c b/clang/test/C/C23/n3037.c
index 8dd46ef69b4146..97320820a3eeba 100644
--- a/clang/test/C/C23/n3037.c
+++ b/clang/test/C/C23/n3037.c
@@ -948,3 +948,16 @@ struct GH199417_3 {
     // it would be better to print the original type.
   } a;                                 // c23-note {{field 'a' has type 'int' here}}
 };
+
+#if __STDC_VERSION__ >= 202311L
+constexpr int n = 1;
+constexpr int m = 2;
+struct GH232748 {
+  struct S { int i : n; } s1; // c23-note {{bit-field 'i' has bit-width 1 here}}
+  struct S { int i : m; } s2; // c23-error {{type 'struct S' has incompatible definitions}}
+                              // c23-note at -1 {{bit-field 'i' has bit-width 2 here}}
+
+  struct M { int i : n; } m1;
+  struct M { int i : n; } m2;
+};
+#endif



More information about the cfe-commits mailing list