Author: Radovan Božić
Date: 2026-08-26T11:04:56+01:00
New Revision: 074e82180c37eff6ba1358fb875459ce93aff521

URL: 
https://github.com/llvm/llvm-project/commit/074e82180c37eff6ba1358fb875459ce93aff521
DIFF: 
https://github.com/llvm/llvm-project/commit/074e82180c37eff6ba1358fb875459ce93aff521.diff

LOG: [clang][analyzer] Model function addresses in constant initializers 
(#217608)

`SValBuilder::getConstantVal()` does not currently handle direct
function addresses, as a result, a const function pointer initialized
with a function is loaded into `UnknownVal`, preventing the analyzer
from resolving and inlining calls through that pointer.

Fixes #216983

Added: 
    clang/test/Analysis/constant-function-pointer.cpp

Modified: 
    clang/lib/StaticAnalyzer/Core/MemRegion.cpp
    clang/lib/StaticAnalyzer/Core/RegionStore.cpp
    clang/lib/StaticAnalyzer/Core/SValBuilder.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp 
b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
index 36a71d510b902..65d4d6651b272 100644
--- a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -1102,7 +1102,9 @@ const VarRegion *MemRegionManager::getVarRegion(const 
VarDecl *D,
   if (D->hasGlobalStorage() && !D->isStaticLocal()) {
     QualType Ty = D->getType();
     assert(!Ty.isNull());
-    if (Ty.isConstQualified()) {
+    // A function reference's binding cannot be changed after initialization,
+    // even though reference types themselves are never const-qualified.
+    if (Ty.isConstQualified() || Ty->isFunctionReferenceType()) {
       sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind);
     } else {
       // Pointer value of C standard streams is usually not modified by calls

diff  --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp 
b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index 01c792a9011f9..6994917470ce8 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -2411,15 +2411,17 @@ SVal 
RegionStoreManager::getBindingForVar(RegionBindingsConstRef B,
   if (isa<StackArgumentsSpaceRegion>(MS))
     return svalBuilder.getRegionValueSymbolVal(R);
 
-  // Is 'VD' declared constant?  If so, retrieve the constant value.
-  if (VD->getType().isConstQualified()) {
+  // Is 'VD' declared constant, or is it a function reference whose
+  // binding is necessarily immutable?  If so, retrieve the value
+  // from its initializer.
+  if (VD->getType().isConstQualified() ||
+      VD->getType()->isFunctionReferenceType()) {
     if (const Expr *Init = VD->getAnyInitializer()) {
       if (std::optional<SVal> V = svalBuilder.getConstantVal(Init))
         return *V;
 
-      // If the variable is const qualified and has an initializer but
-      // we couldn't evaluate initializer to a value, treat the value as
-      // unknown.
+      // If the variable has an immutable binding and an initializer but we
+      // couldn't evaluate the initializer, treat the value as unknown.
       return UnknownVal();
     }
   }

diff  --git a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp 
b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
index 55669c1bef5d1..38dd446f2f9e1 100644
--- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -326,6 +326,14 @@ loc::MemRegionVal SValBuilder::getCXXThis(const 
CXXRecordDecl *D,
 std::optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
   E = E->IgnoreParens();
 
+  // A function used as a constant initializer can either decay to a function
+  // pointer or bind directly to a function reference.
+  if (E->getType()->isFunctionPointerType() || E->getType()->isFunctionType()) 
{
+    if (const auto *FD =
+            dyn_cast_or_null<FunctionDecl>(E->getReferencedDeclOfCallee()))
+      return getFunctionPointer(FD);
+  }
+
   switch (E->getStmtClass()) {
   // Handle expressions that we treat 
diff erently from the AST's constant
   // evaluator.

diff  --git a/clang/test/Analysis/constant-function-pointer.cpp 
b/clang/test/Analysis/constant-function-pointer.cpp
new file mode 100644
index 0000000000000..a35bc01435bfc
--- /dev/null
+++ b/clang/test/Analysis/constant-function-pointer.cpp
@@ -0,0 +1,68 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection \
+// RUN:   -verify %s
+
+template <class T>
+void clang_analyzer_dump(T);
+void clang_analyzer_eval(bool);
+
+using Callback = void (*)(const char *);
+using CallbackRef = void (&)(const char *);
+
+static int Storage;
+
+static void pointerTarget(const char *Value) {
+  int *Ptr = nullptr;
+
+  if (Value)
+    Ptr = &Storage;
+
+  clang_analyzer_dump(Value); // expected-warning{{"pointer"}}
+  *Ptr = 0; // no-warning: Ptr is never null here.
+}
+
+static void referenceTarget(const char *Value) {
+  int *Ptr = nullptr;
+
+  if (Value)
+    Ptr = &Storage;
+
+  clang_analyzer_dump(Value); // expected-warning{{"reference"}}
+  *Ptr = 0; // no-warning: Ptr is never null here.
+}
+
+static Callback const ConstPointer = pointerTarget;
+static Callback const AddressPointer = &pointerTarget;
+static Callback const CastPointer = (Callback)pointerTarget;
+static Callback MutablePointer = pointerTarget;
+static CallbackRef Reference = referenceTarget;
+
+extern CallbackRef ExternalReference;
+
+void testPointers(unsigned Value) {
+  clang_analyzer_eval(ConstPointer == pointerTarget); // 
expected-warning{{TRUE}}
+  ConstPointer("pointer");
+  clang_analyzer_eval(AddressPointer == pointerTarget); // 
expected-warning{{TRUE}}
+  clang_analyzer_eval(CastPointer == pointerTarget); // 
expected-warning{{TRUE}}
+  clang_analyzer_eval(MutablePointer == pointerTarget); // 
expected-warning{{UNKNOWN}}
+}
+
+void testReference(unsigned Value) {
+  clang_analyzer_eval(Reference == referenceTarget); // 
expected-warning{{TRUE}}
+  Reference("reference");
+}
+
+void testExternalReference() {
+  clang_analyzer_eval(ExternalReference == referenceTarget); // 
expected-warning{{UNKNOWN}}
+
+  Callback Before = ExternalReference;
+  clang_analyzer_eval(ExternalReference == Before); // expected-warning{{TRUE}}
+}
+
+void myGlobalFn();
+static const bool Truthy = &myGlobalFn;
+
+// Verify that a function pointer converted to bool
+// is modeled as `true`, not as FunctionCodeRegion.
+void testBoolInitializer() {
+  clang_analyzer_dump(Truthy); // expected-warning{{1 U1b}}
+}


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to