[clang] 0f6110a - No longer diagnose (functionally) empty structures under -Wuninitialized

Aaron Ballman via cfe-commits cfe-commits at lists.llvm.org
Thu Jun 15 04:46:05 PDT 2023


Author: Aaron Ballman
Date: 2023-06-15T07:43:31-04:00
New Revision: 0f6110a4a4250f3d684e166b2d3279fbe4a1f239

URL: https://github.com/llvm/llvm-project/commit/0f6110a4a4250f3d684e166b2d3279fbe4a1f239
DIFF: https://github.com/llvm/llvm-project/commit/0f6110a4a4250f3d684e166b2d3279fbe4a1f239.diff

LOG: No longer diagnose (functionally) empty structures under -Wuninitialized

An empty structure in C has no way to be initialized, so triggering a
-Wuninitialized warning for a variable of empty structure type is not
actionable for users. This silences the false positive warning, which
matches the behavior of GCC as well.

We no longer diagnose if the structure has no members, or has only
zero-sized members (unnamed bit-fields, zero-sized bit-fields, empty
structure types).

Fixes: https://github.com/llvm/llvm-project/issues/26842

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/Analysis/UninitializedValues.cpp
    clang/test/Sema/uninit-variables.c

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3cfacb07bbaa1..451835f8bb39a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -347,6 +347,9 @@ Improvements to Clang's diagnostics
 - When diagnosing a constant expression where an enum without a fixed underlying
   type is set to a value outside the range of the enum's values, clang will now
   print the name of the enum in question.
+- Clang no longer diagnoses a read of an empty structure as use of an
+  uninitialized variable.
+  (`#26842: <https://github.com/llvm/llvm-project/issues/26842>`_)
 
 Bug Fixes in This Version
 -------------------------

diff  --git a/clang/lib/Analysis/UninitializedValues.cpp b/clang/lib/Analysis/UninitializedValues.cpp
index 87765db0c5b2f..3c097f44761f1 100644
--- a/clang/lib/Analysis/UninitializedValues.cpp
+++ b/clang/lib/Analysis/UninitializedValues.cpp
@@ -40,13 +40,31 @@ using namespace clang;
 
 #define DEBUG_LOGGING 0
 
+static bool recordIsNotEmpty(const RecordDecl *RD) {
+  // We consider a record decl to be empty if it contains only unnamed bit-
+  // fields, zero-width fields, and fields of empty record type.
+  for (const auto *FD : RD->fields()) {
+    if (FD->isUnnamedBitfield())
+      continue;
+    if (FD->isZeroSize(FD->getASTContext()))
+      continue;
+    // The only case remaining to check is for a field declaration of record
+    // type and whether that record itself is empty.
+    if (const auto *FieldRD = FD->getType()->getAsRecordDecl();
+        !FieldRD || recordIsNotEmpty(FieldRD))
+      return true;
+  }
+  return false;
+}
+
 static bool isTrackedVar(const VarDecl *vd, const DeclContext *dc) {
   if (vd->isLocalVarDecl() && !vd->hasGlobalStorage() &&
-      !vd->isExceptionVariable() && !vd->isInitCapture() &&
-      !vd->isImplicit() && vd->getDeclContext() == dc) {
+      !vd->isExceptionVariable() && !vd->isInitCapture() && !vd->isImplicit() &&
+      vd->getDeclContext() == dc) {
     QualType ty = vd->getType();
-    return ty->isScalarType() || ty->isVectorType() || ty->isRecordType() ||
-           ty->isRVVType();
+    if (const auto *RD = ty->getAsRecordDecl())
+      return recordIsNotEmpty(RD);
+    return ty->isScalarType() || ty->isVectorType() || ty->isRVVType();
   }
   return false;
 }

diff  --git a/clang/test/Sema/uninit-variables.c b/clang/test/Sema/uninit-variables.c
index fa471499a4375..cba8ee7b19989 100644
--- a/clang/test/Sema/uninit-variables.c
+++ b/clang/test/Sema/uninit-variables.c
@@ -532,3 +532,22 @@ void test_analyzer_noreturn_2(int y) {
   }
   ++x; // no-warning
 }
+
+// Do not diagnose (functionally) empty structures as being uninitalized
+// variables; see GH26842
+struct empty {};
+struct full_of_empty {
+  int : 0;
+  int : 12;
+  struct empty e;
+};
+
+struct empty empty_test_1(void) {
+  struct empty e;
+  return e; // no-warning
+}
+
+struct full_of_empty empty_test_2(void) {
+  struct full_of_empty e;
+  return e; // no-warning
+}


        


More information about the cfe-commits mailing list