https://github.com/bozicrHT updated 
https://github.com/llvm/llvm-project/pull/217608

From 9ef09e0c2a6f72c2ed0ff683c28e610d5fc8ed79 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Radovan=20Bo=C5=BEi=C4=87?= <[email protected]>
Date: Thu, 20 Aug 2026 14:50:13 +0200
Subject: [PATCH 1/2] [clang][analyzer] Model function addresses in constant
 initializers

Teach `SValBuilder::getConstantVal()` to represent direct function
addresses using the existing `FunctionCodeRegion` SVal.

This allows the analyzer to resolve calls through constant function
pointers and preserve caller constraints.
---
 clang/lib/StaticAnalyzer/Core/SValBuilder.cpp |  6 ++
 .../test/Analysis/constant-function-pointer.c | 80 +++++++++++++++++++
 2 files changed, 86 insertions(+)
 create mode 100644 clang/test/Analysis/constant-function-pointer.c

diff --git a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp 
b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
index 57c97b2852445..54b4d9cd4dfed 100644
--- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -326,6 +326,12 @@ loc::MemRegionVal SValBuilder::getCXXThis(const 
CXXRecordDecl *D,
 std::optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
   E = E->IgnoreParens();
 
+  if (E->getType()->isFunctionPointerType()) {
+    if (const auto *FD =
+            dyn_cast_or_null<FunctionDecl>(E->getReferencedDeclOfCallee()))
+      return getFunctionPointer(FD);
+  }
+
   switch (E->getStmtClass()) {
   // Handle expressions that we treat differently from the AST's constant
   // evaluator.
diff --git a/clang/test/Analysis/constant-function-pointer.c 
b/clang/test/Analysis/constant-function-pointer.c
new file mode 100644
index 0000000000000..dcd12eb1122e9
--- /dev/null
+++ b/clang/test/Analysis/constant-function-pointer.c
@@ -0,0 +1,80 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection \
+// RUN:   -analyze-function=test_function_pointer_forms -verify=forms %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection \
+// RUN:   -analyze-function=test_mutable_pointer -verify=mutable %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection \
+// RUN:   -analyze-function=test_no_false_positive -verify=fp %s
+
+void clang_analyzer_checkInlined(int);
+void clang_analyzer_eval(int);
+
+typedef void (*callback)(unsigned);
+
+static void decay_target(unsigned value) {
+  clang_analyzer_checkInlined(value == 1); // forms-warning{{TRUE}}
+}
+
+static callback const decayCallback = decay_target;
+
+static void address_target(unsigned value) {
+  clang_analyzer_checkInlined(value == 2); // forms-warning{{TRUE}}
+}
+
+static callback const addressCallback = &address_target;
+
+static void cast_target(unsigned value) {
+  clang_analyzer_checkInlined(value == 3); // forms-warning{{TRUE}}
+}
+
+static callback const castCallback = (callback)cast_target;
+
+void test_function_pointer_forms(void) {
+  clang_analyzer_eval(decayCallback == decay_target); // forms-warning{{TRUE}}
+  decay_target(1);
+
+  clang_analyzer_eval(addressCallback == address_target); // 
forms-warning{{TRUE}}
+  address_target(2);
+
+  clang_analyzer_eval(castCallback == cast_target); // forms-warning{{TRUE}}
+  castCallback(3);
+}
+
+static callback mutableCallback = decay_target;
+
+void test_mutable_pointer(void) {
+  clang_analyzer_eval(mutableCallback == decay_target); // 
mutable-warning{{UNKNOWN}}
+}
+
+struct St {
+  int f;
+};
+
+static struct St sts[4];
+
+static void helper(unsigned id) {
+  struct St *s = 0;
+
+  if (id < 1)
+    return;
+  if (id < 5)
+    s = &sts[id-1];
+  else if (id == 5)
+    return;
+
+  s->f = 60;
+}
+
+static void notify(unsigned id) {
+  clang_analyzer_checkInlined(id >= 1 && id <= 4); // fp-warning{{TRUE}}
+  helper(id);
+}
+
+static callback const callbackFn = notify;
+
+void test_no_false_positive(unsigned id) {
+  if (id < 1 || id > 4)
+    return;
+
+  clang_analyzer_eval(callbackFn == notify); // fp-warning{{TRUE}}
+  callbackFn(id);
+}

From 7c98a3ca6537a8c2fb73d5aab9b531fcc2351002 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Radovan=20Bo=C5=BEi=C4=87?= <[email protected]>
Date: Mon, 24 Aug 2026 16:43:11 +0200
Subject: [PATCH 2/2] Fix function references in constant initializers

---
 clang/lib/StaticAnalyzer/Core/MemRegion.cpp   |  4 +-
 clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 12 +--
 clang/lib/StaticAnalyzer/Core/SValBuilder.cpp |  5 +-
 .../test/Analysis/constant-function-pointer.c | 80 -------------------
 .../Analysis/constant-function-pointer.cpp    | 64 +++++++++++++++
 5 files changed, 78 insertions(+), 87 deletions(-)
 delete mode 100644 clang/test/Analysis/constant-function-pointer.c
 create mode 100644 clang/test/Analysis/constant-function-pointer.cpp

diff --git a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp 
b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
index 9f27358381738..f57727b50d625 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..72582a93da9d8 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 global function reference whose
+  // binding is necessarily immutable?  If so, retrieve the value from its
+  // initializer.
+  if (VD->getType().isConstQualified() ||
+      (VD->hasGlobalStorage() && 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 54b4d9cd4dfed..7366f1315c9fc 100644
--- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -326,7 +326,10 @@ loc::MemRegionVal SValBuilder::getCXXThis(const 
CXXRecordDecl *D,
 std::optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
   E = E->IgnoreParens();
 
-  if (E->getType()->isFunctionPointerType()) {
+  // 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);
diff --git a/clang/test/Analysis/constant-function-pointer.c 
b/clang/test/Analysis/constant-function-pointer.c
deleted file mode 100644
index dcd12eb1122e9..0000000000000
--- a/clang/test/Analysis/constant-function-pointer.c
+++ /dev/null
@@ -1,80 +0,0 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection \
-// RUN:   -analyze-function=test_function_pointer_forms -verify=forms %s
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection \
-// RUN:   -analyze-function=test_mutable_pointer -verify=mutable %s
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection \
-// RUN:   -analyze-function=test_no_false_positive -verify=fp %s
-
-void clang_analyzer_checkInlined(int);
-void clang_analyzer_eval(int);
-
-typedef void (*callback)(unsigned);
-
-static void decay_target(unsigned value) {
-  clang_analyzer_checkInlined(value == 1); // forms-warning{{TRUE}}
-}
-
-static callback const decayCallback = decay_target;
-
-static void address_target(unsigned value) {
-  clang_analyzer_checkInlined(value == 2); // forms-warning{{TRUE}}
-}
-
-static callback const addressCallback = &address_target;
-
-static void cast_target(unsigned value) {
-  clang_analyzer_checkInlined(value == 3); // forms-warning{{TRUE}}
-}
-
-static callback const castCallback = (callback)cast_target;
-
-void test_function_pointer_forms(void) {
-  clang_analyzer_eval(decayCallback == decay_target); // forms-warning{{TRUE}}
-  decay_target(1);
-
-  clang_analyzer_eval(addressCallback == address_target); // 
forms-warning{{TRUE}}
-  address_target(2);
-
-  clang_analyzer_eval(castCallback == cast_target); // forms-warning{{TRUE}}
-  castCallback(3);
-}
-
-static callback mutableCallback = decay_target;
-
-void test_mutable_pointer(void) {
-  clang_analyzer_eval(mutableCallback == decay_target); // 
mutable-warning{{UNKNOWN}}
-}
-
-struct St {
-  int f;
-};
-
-static struct St sts[4];
-
-static void helper(unsigned id) {
-  struct St *s = 0;
-
-  if (id < 1)
-    return;
-  if (id < 5)
-    s = &sts[id-1];
-  else if (id == 5)
-    return;
-
-  s->f = 60;
-}
-
-static void notify(unsigned id) {
-  clang_analyzer_checkInlined(id >= 1 && id <= 4); // fp-warning{{TRUE}}
-  helper(id);
-}
-
-static callback const callbackFn = notify;
-
-void test_no_false_positive(unsigned id) {
-  if (id < 1 || id > 4)
-    return;
-
-  clang_analyzer_eval(callbackFn == notify); // fp-warning{{TRUE}}
-  callbackFn(id);
-}
diff --git a/clang/test/Analysis/constant-function-pointer.cpp 
b/clang/test/Analysis/constant-function-pointer.cpp
new file mode 100644
index 0000000000000..603e04b1bd6c9
--- /dev/null
+++ b/clang/test/Analysis/constant-function-pointer.cpp
@@ -0,0 +1,64 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection \
+// RUN:   -verify %s
+
+void clang_analyzer_checkInlined(bool);
+void clang_analyzer_eval(bool);
+
+typedef void (*Callback)(unsigned);
+typedef void (&CallbackRef)(unsigned);
+
+static int Storage;
+
+static void pointerTarget(unsigned Value) {
+  int *Ptr = nullptr;
+
+  if (Value == 1)
+    Ptr = &Storage;
+
+  clang_analyzer_checkInlined(Value == 1); // expected-warning{{TRUE}}
+  *Ptr = 0;
+}
+
+static void referenceTarget(unsigned Value) {
+  int *Ptr = nullptr;
+
+  if (Value == 1)
+    Ptr = &Storage;
+
+  clang_analyzer_checkInlined(Value == 1); // expected-warning{{TRUE}}
+  *Ptr = 0;
+}
+
+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) {
+  if (Value != 1)
+    return;
+
+  clang_analyzer_eval(ConstPointer == pointerTarget); // 
expected-warning{{TRUE}}
+  ConstPointer(Value);
+  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) {
+  if (Value != 1)
+    return;
+
+  clang_analyzer_eval(Reference == referenceTarget); // 
expected-warning{{TRUE}}
+  Reference(Value);
+}
+
+void testExternalReference() {
+  clang_analyzer_eval(ExternalReference == referenceTarget); // 
expected-warning{{UNKNOWN}}
+
+  Callback Before = ExternalReference;
+  clang_analyzer_eval(ExternalReference == Before); // expected-warning{{TRUE}}
+}

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

Reply via email to