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
