Skip to content

Conversation

@jimmy2683
Copy link

c is uninitialized and used in the main function.

Options: -Wall -Wextra -Wuninitialized -Wconditional-uninitialized -O2

Link: https://godbolt.org/z/1aMWdEfjr

Code:

int b[24] = {0};
int main() {
  int c[24];
  for (int d = 0; d < 24; ++d)
    b[d] = c[d];
  __builtin_printf("%d\n", b[0]);
  return 0;
}

This might be related to #66448

Problem

Clang does not warn about uninitialized array access while GCC does:

int main() {
    int arr[5];
    int x = arr[0];  // GCC warns: 'arr' may be used uninitialized
                     // Clang: no warning
    return x;
}

Root Cause Analysis

The issue was in clang/lib/Analysis/UninitializedValues.cpp:

  1. Array Tracking: Arrays were included in isTrackedVar() but not properly classified
  2. Classification Issue: Array subscript expressions like arr[0] were defaulting to "Init" instead of "Use"
  3. Missing Logic: No specific handling for ArraySubscriptExpr in classification

Solution

Key Changes Made

  1. Enhanced isTrackedVar() (line ~69):
bool isArray = ty->isArrayType();
bool result = ty->isScalarType() || ty->isVectorType() || ty->isRVVSizelessBuiltinType() || isArray;
return result;
  1. Fixed ClassifyRefs::get() (line ~298):
// Default to Use instead of Init for arrays - CRITICAL FIX
if (VD->getType()->isArrayType()) {
    return Use;
}
  1. Added Array Subscript Handling in classify() (line ~332):
// Handle array subscripts - THE KEY FIX
if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
    // For ANY array subscript expression, the base array is being USED
    FindVarResult Var = findVar(ASE->getBase(), DC);
    if (const VarDecl *VD = Var.getDecl()) {
        if (VD->getType()->isArrayType()) {
            // Directly mark the array DeclRefExpr as Use
            if (const DeclRefExpr *DRE = Var.getDeclRefExpr()) {
                Classification[DRE] = Use;
            }
        }
    }
    // ... rest of processing
}
  1. Improved Binary Operator Handling (line ~383):
// For array subscript expressions on LHS of assignment, don't classify as use
if (isa<ArraySubscriptExpr>(BO->getLHS())) {
    // Don't classify array base as use when it's being assigned to
    // But we still need to visit the index expression
    if (auto *ASE = dyn_cast<ArraySubscriptExpr>(BO->getLHS())) {
        Visit(ASE->getIdx());
    }
} else {
    classify(BO->getLHS(), Ignore);
}
// ALWAYS visit the right-hand side - this is crucial for array subscripts
Visit(BO->getRHS());

Test Results

Before Fix:

$ clang -Wuninitialized problem.cpp
# No warnings

After Fix:

$ clang -Wuninitialized problem.cpp
problem.cpp:5:12: warning: variable 'c' is uninitialized when used here [-Wuninitialized]
    5 |     b[d] = c[d];
      |            ^
problem.cpp:3:3: note: variable 'c' is declared here
    3 |   int c[24];
      |   ^
1 warning generated.

@github-actions
Copy link

github-actions bot commented Nov 7, 2025

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:analysis labels Nov 7, 2025
@llvmbot
Copy link
Member

llvmbot commented Nov 7, 2025

@llvm/pr-subscribers-clang-analysis

@llvm/pr-subscribers-clang

Author: Jimmy2683 (jimmy2683)

Changes

c is uninitialized and used in the main function.

Options: -Wall -Wextra -Wuninitialized -Wconditional-uninitialized -O2

Link: https://godbolt.org/z/1aMWdEfjr

Code:

int b[24] = {0};
int main() {
  int c[24];
  for (int d = 0; d &lt; 24; ++d)
    b[d] = c[d];
  __builtin_printf("%d\n", b[0]);
  return 0;
}

This might be related to #66448

Problem

Clang does not warn about uninitialized array access while GCC does:

int main() {
    int arr[5];
    int x = arr[0];  // GCC warns: 'arr' may be used uninitialized
                     // Clang: no warning
    return x;
}

Root Cause Analysis

The issue was in clang/lib/Analysis/UninitializedValues.cpp:

  1. Array Tracking: Arrays were included in isTrackedVar() but not properly classified
  2. Classification Issue: Array subscript expressions like arr[0] were defaulting to "Init" instead of "Use"
  3. Missing Logic: No specific handling for ArraySubscriptExpr in classification

Solution

Key Changes Made

  1. Enhanced isTrackedVar() (line ~69):
bool isArray = ty-&gt;isArrayType();
bool result = ty-&gt;isScalarType() || ty-&gt;isVectorType() || ty-&gt;isRVVSizelessBuiltinType() || isArray;
return result;
  1. Fixed ClassifyRefs::get() (line ~298):
// Default to Use instead of Init for arrays - CRITICAL FIX
if (VD-&gt;getType()-&gt;isArrayType()) {
    return Use;
}
  1. Added Array Subscript Handling in classify() (line ~332):
// Handle array subscripts - THE KEY FIX
if (const auto *ASE = dyn_cast&lt;ArraySubscriptExpr&gt;(E)) {
    // For ANY array subscript expression, the base array is being USED
    FindVarResult Var = findVar(ASE-&gt;getBase(), DC);
    if (const VarDecl *VD = Var.getDecl()) {
        if (VD-&gt;getType()-&gt;isArrayType()) {
            // Directly mark the array DeclRefExpr as Use
            if (const DeclRefExpr *DRE = Var.getDeclRefExpr()) {
                Classification[DRE] = Use;
            }
        }
    }
    // ... rest of processing
}
  1. Improved Binary Operator Handling (line ~383):
// For array subscript expressions on LHS of assignment, don't classify as use
if (isa&lt;ArraySubscriptExpr&gt;(BO-&gt;getLHS())) {
    // Don't classify array base as use when it's being assigned to
    // But we still need to visit the index expression
    if (auto *ASE = dyn_cast&lt;ArraySubscriptExpr&gt;(BO-&gt;getLHS())) {
        Visit(ASE-&gt;getIdx());
    }
} else {
    classify(BO-&gt;getLHS(), Ignore);
}
// ALWAYS visit the right-hand side - this is crucial for array subscripts
Visit(BO-&gt;getRHS());

Test Results

Before Fix:

$ clang -Wuninitialized problem.cpp
# No warnings

After Fix:

$ clang -Wuninitialized problem.cpp
problem.cpp:5:12: warning: variable 'c' is uninitialized when used here [-Wuninitialized]
    5 |     b[d] = c[d];
      |            ^
problem.cpp:3:3: note: variable 'c' is declared here
    3 |   int c[24];
      |   ^
1 warning generated.

Full diff: https://github.com/llvm/llvm-project/pull/166932.diff

1 Files Affected:

  • (modified) clang/lib/Analysis/UninitializedValues.cpp (+121-8)
diff --git a/clang/lib/Analysis/UninitializedValues.cpp b/clang/lib/Analysis/UninitializedValues.cpp
index f6b1c67ab20c3..7a3decc927a91 100644
--- a/clang/lib/Analysis/UninitializedValues.cpp
+++ b/clang/lib/Analysis/UninitializedValues.cpp
@@ -62,7 +62,18 @@ static bool isTrackedVar(const VarDecl *vd, const DeclContext *dc) {
     QualType ty = vd->getType();
     if (const auto *RD = ty->getAsRecordDecl())
       return recordIsNotEmpty(RD);
-    return ty->isScalarType() || ty->isVectorType() || ty->isRVVSizelessBuiltinType();
+    
+    bool isArray = ty->isArrayType();
+    bool result = ty->isScalarType() || ty->isVectorType() || ty->isRVVSizelessBuiltinType() || isArray;
+    
+    // Debug output
+    #if DEBUG_LOGGING
+    llvm::errs() << "DEBUG isTrackedVar: " << vd->getNameAsString() 
+                 << " isArray=" << isArray 
+                 << " result=" << result << "\n";
+    #endif
+    
+    return result;
   }
   return false;
 }
@@ -187,8 +198,8 @@ void CFGBlockValues::computeSetOfDeclarations(const DeclContext &dc) {
 static void printVector(const CFGBlock *block, ValueVector &bv,
                         unsigned num) {
   llvm::errs() << block->getBlockID() << " :";
-  for (const auto &i : bv)
-    llvm::errs() << ' ' << i;
+  for (unsigned i = 0, e = bv.size(); i != e; ++i)
+    llvm::errs() << ' ' << bv[i];
   llvm::errs() << " : " << num << '\n';
 }
 #endif
@@ -291,6 +302,7 @@ class ClassifyRefs : public StmtVisitor<ClassifyRefs> {
 public:
   ClassifyRefs(AnalysisDeclContext &AC) : DC(cast<DeclContext>(AC.getDecl())) {}
 
+  void VisitArraySubscriptExpr(ArraySubscriptExpr *ASE);
   void VisitDeclStmt(DeclStmt *DS);
   void VisitUnaryOperator(UnaryOperator *UO);
   void VisitBinaryOperator(BinaryOperator *BO);
@@ -303,13 +315,19 @@ class ClassifyRefs : public StmtVisitor<ClassifyRefs> {
   Class get(const DeclRefExpr *DRE) const {
     llvm::DenseMap<const DeclRefExpr*, Class>::const_iterator I
         = Classification.find(DRE);
-    if (I != Classification.end())
+    if (I != Classification.end()) {
       return I->second;
+    }
 
     const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
     if (!VD || !isTrackedVar(VD))
       return Ignore;
 
+    // Default to Use instead of Init for arrays - CRITICAL FIX
+    if (VD->getType()->isArrayType()) {
+      return Use;
+    }
+
     return Init;
   }
 };
@@ -331,6 +349,27 @@ static const DeclRefExpr *getSelfInitExpr(VarDecl *VD) {
 void ClassifyRefs::classify(const Expr *E, Class C) {
   // The result of a ?: could also be an lvalue.
   E = E->IgnoreParens();
+  
+  // Handle array subscripts - THE KEY FIX
+  if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
+    // For ANY array subscript expression, the base array is being USED
+    // This is the critical fix - don't check C, just mark as Use
+    FindVarResult Var = findVar(ASE->getBase(), DC);
+    if (const VarDecl *VD = Var.getDecl()) {
+      if (VD->getType()->isArrayType()) {
+        // Directly mark the array DeclRefExpr as Use
+        if (const DeclRefExpr *DRE = Var.getDeclRefExpr()) {
+          Classification[DRE] = Use;
+        }
+      }
+    }
+    
+    // Process base and index normally
+    classify(ASE->getBase(), C);
+    Visit(const_cast<Expr*>(ASE->getIdx()));
+    return;
+  }
+  
   if (const auto *CO = dyn_cast<ConditionalOperator>(E)) {
     classify(CO->getTrueExpr(), C);
     classify(CO->getFalseExpr(), C);
@@ -376,6 +415,18 @@ void ClassifyRefs::classify(const Expr *E, Class C) {
   }
 }
 
+void ClassifyRefs::VisitArraySubscriptExpr(ArraySubscriptExpr *ASE) {
+  // Debug output
+  #if DEBUG_LOGGING
+  llvm::errs() << "DEBUG ClassifyRefs::VisitArraySubscriptExpr called\n";
+  #endif
+  
+  // For array subscript expressions, classify the base as a use
+  classify(ASE->getBase(), Use);
+  // Also visit the index expression
+  Visit(ASE->getIdx());
+}
+
 void ClassifyRefs::VisitDeclStmt(DeclStmt *DS) {
   for (auto *DI : DS->decls()) {
     auto *VD = dyn_cast<VarDecl>(DI);
@@ -393,10 +444,27 @@ void ClassifyRefs::VisitBinaryOperator(BinaryOperator *BO) {
   // use.
   if (BO->isCompoundAssignmentOp())
     classify(BO->getLHS(), Use);
-  else if (BO->getOpcode() == BO_Assign || BO->getOpcode() == BO_Comma)
-    classify(BO->getLHS(), Ignore);
+  else if (BO->getOpcode() == BO_Assign || BO->getOpcode() == BO_Comma) {
+    // For array subscript expressions on LHS of assignment, don't classify as use
+    if (isa<ArraySubscriptExpr>(BO->getLHS())) {
+      // Don't classify array base as use when it's being assigned to
+      // But we still need to visit the index expression
+      if (auto *ASE = dyn_cast<ArraySubscriptExpr>(BO->getLHS())) {
+        Visit(ASE->getIdx());
+      }
+    } else {
+      classify(BO->getLHS(), Ignore);
+    }
+    // ALWAYS visit the right-hand side - this is crucial for array subscripts
+    Visit(BO->getRHS());
+  } else {
+    // For all other binary operators, visit both operands normally
+    Visit(BO->getLHS());
+    Visit(BO->getRHS());
+  }
 }
 
+
 void ClassifyRefs::VisitUnaryOperator(UnaryOperator *UO) {
   // Increment and decrement are uses despite there being no lvalue-to-rvalue
   // conversion.
@@ -491,6 +559,7 @@ class TransferFunctions : public StmtVisitor<TransferFunctions> {
   void reportConstRefUse(const Expr *ex, const VarDecl *vd);
   void reportConstPtrUse(const Expr *ex, const VarDecl *vd);
 
+  void VisitArraySubscriptExpr(ArraySubscriptExpr *ASE);
   void VisitBinaryOperator(BinaryOperator *bo);
   void VisitBlockExpr(BlockExpr *be);
   void VisitCallExpr(CallExpr *ce);
@@ -661,10 +730,54 @@ class TransferFunctions : public StmtVisitor<TransferFunctions> {
 
 } // namespace
 
+void TransferFunctions::VisitArraySubscriptExpr(ArraySubscriptExpr *ASE) {
+  // Debug output
+  #if DEBUG_LOGGING
+  llvm::errs() << "DEBUG TransferFunctions::VisitArraySubscriptExpr called\n";
+  #endif
+  
+  // Handle array subscript expressions like arr[i]
+  // Check if the base array variable is uninitialized
+  FindVarResult Var = findVar(ASE->getBase());
+  
+  #if DEBUG_LOGGING
+  llvm::errs() << "DEBUG: FindVar result: " << (Var.getDecl() ? "found" : "not found") << "\n";
+  if (Var.getDecl()) {
+    llvm::errs() << "DEBUG: Variable name: " << Var.getDecl()->getNameAsString() << "\n";
+    llvm::errs() << "DEBUG: Is array type: " << Var.getDecl()->getType()->isArrayType() << "\n";
+  }
+  #endif
+  
+  if (const VarDecl *VD = Var.getDecl()) {
+    if (VD->getType()->isArrayType()) {
+      #if DEBUG_LOGGING
+      llvm::errs() << "DEBUG: Reporting array use for " << VD->getNameAsString() << "\n";
+      #endif
+      reportUse(ASE, VD);
+    }
+  }
+  
+  // Also visit index expression
+  Visit(ASE->getIdx());
+}
+
 void TransferFunctions::reportUse(const Expr *ex, const VarDecl *vd) {
+  #if DEBUG_LOGGING
+  llvm::errs() << "DEBUG TransferFunctions::reportUse for " << vd->getNameAsString() << "\n";
+  #endif
+  
   Value v = vals[vd];
-  if (isUninitialized(v))
+  
+  #if DEBUG_LOGGING
+  llvm::errs() << "DEBUG: Variable " << vd->getNameAsString() << " has value " << v << "\n";
+  #endif
+  
+  if (isUninitialized(v)) {
+    #if DEBUG_LOGGING
+    llvm::errs() << "DEBUG: Reporting uninitialized use of " << vd->getNameAsString() << "\n";
+    #endif
     handler.handleUseOfUninitVariable(vd, getUninitUse(ex, vd, v));
+  }
 }
 
 void TransferFunctions::reportConstRefUse(const Expr *ex, const VarDecl *vd) {
@@ -967,4 +1080,4 @@ void clang::runUninitializedVariablesAnalysis(
     }
 }
 
-UninitVariablesHandler::~UninitVariablesHandler() = default;
+UninitVariablesHandler::~UninitVariablesHandler() = default;
\ No newline at end of file

@zwuis
Copy link
Contributor

zwuis commented Nov 7, 2025

Thank you for your patch!

If AI was used to create PR, please disclose it. And to what extent it was used.

Please do not use AI to generate PR description. Please see https://llvm.org/docs/DeveloperPolicy.html#commit-messages for how to write it.

Please add tests to "clang/test".

Please add a release note entry to "clang/docs/ReleaseNotes.rst".

Please remove unrelated changes.

@jimmy2683 jimmy2683 closed this Nov 7, 2025
@jimmy2683 jimmy2683 deleted the issue-165239 branch November 7, 2025 16:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:analysis clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants