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: `_) 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 +}