[clang] [Clang] [NFC] Add `APValue::visit()` (PR #230243)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 8 11:51:06 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Ambrose Leeb (Sirraide)
<details>
<summary>Changes</summary>
This patch adds a helper function to `APValue` that walks the value recursively and visits all nested values. This functionality is currently used by `LinkageComputer::getLVForValue()` and it looks like I will be needing it for a patch soon.
Assisted-by: Codex
---
Full diff: https://github.com/llvm/llvm-project/pull/230243.diff
4 Files Affected:
- (modified) clang/include/clang/AST/APValue.h (+6)
- (modified) clang/lib/AST/APValue.cpp (+111-91)
- (added) clang/unittests/AST/APValueTest.cpp (+73)
- (modified) clang/unittests/AST/CMakeLists.txt (+1)
``````````diff
diff --git a/clang/include/clang/AST/APValue.h b/clang/include/clang/AST/APValue.h
index 35576a4b6403e9..571eaa146f56ae 100644
--- a/clang/include/clang/AST/APValue.h
+++ b/clang/include/clang/AST/APValue.h
@@ -22,6 +22,7 @@
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/bit.h"
#include "llvm/Support/AlignOf.h"
#include "llvm/Support/Compiler.h"
@@ -535,6 +536,11 @@ class LLVM_ATTRIBUTE_WARN_UNUSED APValue {
ValueKind getKind() const { return Kind; }
+ /// Visit this value and every recursively nested value.
+ ///
+ /// Visitation stops if \p Visitor returns false.
+ void visit(llvm::function_ref<bool(const APValue &)> Visitor) const;
+
bool isAbsent() const { return Kind == None; }
bool isIndeterminate() const { return Kind == Indeterminate; }
bool hasValue() const { return Kind != None && Kind != Indeterminate; }
diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp
index 479c3c0d628963..97023be49c715b 100644
--- a/clang/lib/AST/APValue.cpp
+++ b/clang/lib/AST/APValue.cpp
@@ -21,6 +21,67 @@
#include "llvm/Support/raw_ostream.h"
using namespace clang;
+static bool visitAPValue(const APValue &Value,
+ llvm::function_ref<bool(const APValue &)> Visitor) {
+ if (!Visitor(Value))
+ return false;
+
+ switch (Value.getKind()) {
+ case APValue::None:
+ case APValue::Indeterminate:
+ case APValue::Int:
+ case APValue::Float:
+ case APValue::FixedPoint:
+ case APValue::ComplexInt:
+ case APValue::ComplexFloat:
+ case APValue::LValue:
+ case APValue::MemberPointer:
+ case APValue::AddrLabelDiff:
+ case APValue::Reflection:
+ return true;
+
+ case APValue::Vector:
+ for (unsigned I = 0, N = Value.getVectorLength(); I != N; ++I)
+ if (!visitAPValue(Value.getVectorElt(I), Visitor))
+ return false;
+ return true;
+
+ case APValue::Matrix:
+ for (unsigned I = 0, N = Value.getMatrixNumElements(); I != N; ++I)
+ if (!visitAPValue(Value.getMatrixElt(I), Visitor))
+ return false;
+ return true;
+
+ case APValue::Array:
+ for (unsigned I = 0, N = Value.getArrayInitializedElts(); I != N; ++I)
+ if (!visitAPValue(Value.getArrayInitializedElt(I), Visitor))
+ return false;
+ return !Value.hasArrayFiller() ||
+ visitAPValue(Value.getArrayFiller(), Visitor);
+
+ case APValue::Struct:
+ for (unsigned I = 0, N = Value.getStructNumBases(); I != N; ++I)
+ if (!visitAPValue(Value.getStructBase(I), Visitor))
+ return false;
+ for (unsigned I = 0, N = Value.getStructNumFields(); I != N; ++I)
+ if (!visitAPValue(Value.getStructField(I), Visitor))
+ return false;
+ for (unsigned I = 0, N = Value.getStructNumVirtualBases(); I != N; ++I)
+ if (!visitAPValue(Value.getStructVirtualBase(I), Visitor))
+ return false;
+ return true;
+
+ case APValue::Union:
+ return !Value.getUnionField() ||
+ visitAPValue(Value.getUnionValue(), Visitor);
+ }
+ llvm_unreachable("unknown APValue kind");
+}
+
+void APValue::visit(llvm::function_ref<bool(const APValue &)> Visitor) const {
+ visitAPValue(*this, Visitor);
+}
+
/// The identity of a type_info object depends on the canonical unqualified
/// type only.
TypeInfoLValue::TypeInfoLValue(const Type *T)
@@ -1204,99 +1265,58 @@ LinkageInfo LinkageComputer::getLVForValue(const APValue &V,
LVComputationKind computation) {
LinkageInfo LV = LinkageInfo::external();
- auto MergeLV = [&](LinkageInfo MergeLV) {
- LV.merge(MergeLV);
- return LV.getLinkage() == Linkage::Internal;
- };
- auto Merge = [&](const APValue &V) {
- return MergeLV(getLVForValue(V, computation));
- };
-
- switch (V.getKind()) {
- case APValue::None:
- case APValue::Indeterminate:
- case APValue::Int:
- case APValue::Float:
- case APValue::FixedPoint:
- case APValue::ComplexInt:
- case APValue::ComplexFloat:
- case APValue::Vector:
- case APValue::Reflection:
- case APValue::Matrix:
- break;
-
- case APValue::AddrLabelDiff:
- // Even for an inline function, it's not reasonable to treat a difference
- // between the addresses of labels as an external value.
- return LinkageInfo::internal();
-
- case APValue::Struct: {
- for (unsigned I = 0, N = V.getStructNumBases(); I != N; ++I)
- if (Merge(V.getStructBase(I)))
- break;
- for (unsigned I = 0, N = V.getStructNumFields(); I != N; ++I)
- if (Merge(V.getStructField(I)))
- break;
- for (unsigned I = 0, N = V.getStructNumVirtualBases(); I != N; ++I)
- if (Merge(V.getStructVirtualBase(I)))
- break;
- break;
- }
-
- case APValue::Union:
- if (V.getUnionField())
- Merge(V.getUnionValue());
- break;
-
- case APValue::Array: {
- for (unsigned I = 0, N = V.getArrayInitializedElts(); I != N; ++I)
- if (Merge(V.getArrayInitializedElt(I)))
- break;
- if (V.hasArrayFiller())
- Merge(V.getArrayFiller());
- break;
- }
+ auto MergeLV = [&](LinkageInfo MergeLV) { LV.merge(MergeLV); };
+ V.visit([&](const APValue &Value) {
+ switch (Value.getKind()) {
+ case APValue::AddrLabelDiff:
+ // Even for an inline function, it's not reasonable to treat a difference
+ // between the addresses of labels as an external value.
+ LV = LinkageInfo::internal();
+ return false;
- case APValue::LValue: {
- if (!V.getLValueBase()) {
- // Null or absolute address: this is external.
- } else if (const auto *VD =
- V.getLValueBase().dyn_cast<const ValueDecl *>()) {
- if (VD && MergeLV(getLVForDecl(VD, computation)))
- break;
- } else if (const auto TI = V.getLValueBase().dyn_cast<TypeInfoLValue>()) {
- if (MergeLV(getLVForType(*TI.getType(), computation)))
- break;
- } else if (const Expr *E = V.getLValueBase().dyn_cast<const Expr *>()) {
- // Almost all expression bases are internal. The exception is
- // lifetime-extended temporaries.
- // FIXME: These should be modeled as having the
- // LifetimeExtendedTemporaryDecl itself as the base.
- // FIXME: If we permit Objective-C object literals in template arguments,
- // they should not imply internal linkage.
- auto *MTE = dyn_cast<MaterializeTemporaryExpr>(E);
- if (!MTE || MTE->getStorageDuration() == SD_FullExpression)
- return LinkageInfo::internal();
- if (MergeLV(getLVForDecl(MTE->getExtendingDecl(), computation)))
- break;
- } else {
- assert(V.getLValueBase().is<DynamicAllocLValue>() &&
- "unexpected LValueBase kind");
- return LinkageInfo::internal();
+ case APValue::LValue:
+ if (!Value.getLValueBase()) {
+ // Null or absolute address: this is external.
+ } else if (const auto *VD =
+ Value.getLValueBase().dyn_cast<const ValueDecl *>()) {
+ MergeLV(getLVForDecl(VD, computation));
+ } else if (const auto TI =
+ Value.getLValueBase().dyn_cast<TypeInfoLValue>()) {
+ MergeLV(getLVForType(*TI.getType(), computation));
+ } else if (const Expr *E =
+ Value.getLValueBase().dyn_cast<const Expr *>()) {
+ // Almost all expression bases are internal. The exception is
+ // lifetime-extended temporaries.
+ // FIXME: These should be modeled as having the
+ // LifetimeExtendedTemporaryDecl itself as the base.
+ // FIXME: If we permit Objective-C object literals in template
+ // arguments, they should not imply internal linkage.
+ auto *MTE = dyn_cast<MaterializeTemporaryExpr>(E);
+ if (!MTE || MTE->getStorageDuration() == SD_FullExpression)
+ LV = LinkageInfo::internal();
+ else
+ MergeLV(getLVForDecl(MTE->getExtendingDecl(), computation));
+ } else {
+ assert(Value.getLValueBase().is<DynamicAllocLValue>() &&
+ "unexpected LValueBase kind");
+ LV = LinkageInfo::internal();
+ }
+ // The lvalue path doesn't matter: pointers to all subobjects always have
+ // the same visibility as pointers to the complete object.
+ return LV.getLinkage() != Linkage::Internal;
+
+ case APValue::MemberPointer:
+ if (const NamedDecl *D = Value.getMemberPointerDecl())
+ MergeLV(getLVForDecl(D, computation));
+ // Note that we could have a base-to-derived conversion here to a member
+ // of a derived class with less linkage/visibility. That's covered by the
+ // linkage and visibility of the value's type.
+ return LV.getLinkage() != Linkage::Internal;
+
+ default:
+ return true;
}
- // The lvalue path doesn't matter: pointers to all subobjects always have
- // the same visibility as pointers to the complete object.
- break;
- }
-
- case APValue::MemberPointer:
- if (const NamedDecl *D = V.getMemberPointerDecl())
- MergeLV(getLVForDecl(D, computation));
- // Note that we could have a base-to-derived conversion here to a member of
- // a derived class with less linkage/visibility. That's covered by the
- // linkage and visibility of the value's type.
- break;
- }
+ });
return LV;
}
diff --git a/clang/unittests/AST/APValueTest.cpp b/clang/unittests/AST/APValueTest.cpp
new file mode 100644
index 00000000000000..726fca8e5c9aa5
--- /dev/null
+++ b/clang/unittests/AST/APValueTest.cpp
@@ -0,0 +1,73 @@
+//===- unittests/AST/APValueTest.cpp - APValue tests ----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/APValue.h"
+#include "llvm/ADT/DenseMap.h"
+#include "gtest/gtest.h"
+#include <cstdint>
+#include <utility>
+
+using namespace clang;
+
+namespace {
+
+static APValue makeInt(uint64_t Value) {
+ return APValue(llvm::APSInt(llvm::APInt(32, Value), /*IsUnsigned=*/false));
+}
+
+static APValue makeNestedValue() {
+ APValue VectorElements[] = {makeInt(1), makeInt(2)};
+ APValue Vector(VectorElements, 2);
+
+ APValue MatrixElements[] = {makeInt(3), makeInt(4), makeInt(5), makeInt(6)};
+ APValue Matrix(MatrixElements, 2, 2);
+
+ APValue Base(APValue::UninitStruct(), 0, 2);
+ Base.getStructField(0) = std::move(Vector);
+ Base.getStructField(1) = std::move(Matrix);
+
+ APValue Array(APValue::UninitArray(), 1, 2);
+ Array.getArrayInitializedElt(0) = makeInt(7);
+ Array.getArrayFiller() = makeInt(0);
+
+ APValue Union(static_cast<const FieldDecl *>(nullptr));
+ APValue VirtualBase(APValue::UninitStruct(), 0, 1);
+ VirtualBase.getStructField(0) = std::move(Union);
+
+ APValue Result(APValue::UninitStruct(), 1, 1, 1);
+ Result.getStructBase(0) = std::move(Base);
+ Result.getStructField(0) = std::move(Array);
+ Result.getStructVirtualBase(0) = std::move(VirtualBase);
+ return Result;
+}
+
+TEST(APValueTest, VisitCountsNestedValueKinds) {
+ APValue Value = makeNestedValue();
+ llvm::DenseMap<APValue::ValueKind, unsigned> Counts;
+ Value.visit([&](const APValue &SubValue) {
+ ++Counts[SubValue.getKind()];
+ return true;
+ });
+
+ const llvm::DenseMap<APValue::ValueKind, unsigned> Expected = {
+ {APValue::Int, 8}, {APValue::Vector, 1}, {APValue::Matrix, 1},
+ {APValue::Array, 1}, {APValue::Struct, 3}, {APValue::Union, 1},
+ };
+ EXPECT_EQ(Counts, Expected);
+}
+
+TEST(APValueTest, VisitStopsWhenCallbackReturnsFalse) {
+ APValue Value = makeNestedValue();
+ for (unsigned StopAfter : {1u, 2u, 5u}) {
+ unsigned NumVisited = 0;
+ Value.visit([&](const APValue &) { return ++NumVisited < StopAfter; });
+ EXPECT_EQ(NumVisited, StopAfter);
+ }
+}
+
+} // namespace
diff --git a/clang/unittests/AST/CMakeLists.txt b/clang/unittests/AST/CMakeLists.txt
index e726cc3299a5a5..8e4cb5af59c923 100644
--- a/clang/unittests/AST/CMakeLists.txt
+++ b/clang/unittests/AST/CMakeLists.txt
@@ -1,6 +1,7 @@
add_subdirectory(ByteCode)
add_clang_unittest(ASTTests
+ APValueTest.cpp
ASTContextParentMapTest.cpp
ASTDumperTest.cpp
StringLiteral.cpp
``````````
</details>
https://github.com/llvm/llvm-project/pull/230243
More information about the cfe-commits
mailing list