Skip to content

Commit bde5717

Browse files
authored
[analyzer][NFC] Rework SVal kind representation (#71039)
The goal of this patch is to refine how the `SVal` base and sub-kinds are represented by forming one unified enum describing the possible SVals. This means that the `unsigned SVal::Kind` and the attached bit-packing semantics would be replaced by a single unified enum. This is more conventional and leads to a better debugging experience by default. This eases the need of using debug pretty-printers, or the use of runtime functions doing the printing for us like we do today by calling `Val.dump()` whenever we inspect the values. Previously, the first 2 bits of the `unsigned SVal::Kind` discriminated the following quartet: `UndefinedVal`, `UnknownVal`, `Loc`, or `NonLoc`. The rest of the upper bits represented the sub-kind, where the value represented the index among only the `Loc`s or `NonLoc`s, effectively attaching 2 meanings of the upper bits depending on the base-kind. We don't need to pack these bits, as we have plenty even if we would use just a plan-old `unsigned char`. Consequently, in this patch, I propose to lay out all the (non-abstract) `SVal` kinds into a single enum, along with some metadata (`BEGIN_Loc`, `END_Loc`, `BEGIN_NonLoc`, `END_NonLoc`) artificial enum values, similar how we do with the `MemRegions`. Note that in the unified `SVal::Kind` enum, to differentiate `nonloc::ConcreteInt` from `loc::ConcreteInt`, I had to prefix them with `Loc` and `NonLoc` to resolve this ambiguity. This should not surface in general, because I'm replacing the `nonloc::Kind` enum items with `inline constexpr` global constants to mimic the original behavior - and offer nicer spelling to these enum values. Some `SVal` constructors were not marked explicit, which I now mark as such to follow best practices, and marked others as `/*implicit*/` to clarify the intent. During refactoring, I also found at least one function not marked `LLVM_ATTRIBUTE_RETURNS_NONNULL`, so I did that. The `TypeRetrievingVisitor` visitor had some accidental dead code, namely: `VisitNonLocConcreteInt` and `VisitLocConcreteInt`. Previously, the `SValVisitor` expected visit handlers of `VisitNonLocXXXXX(nonloc::XXXXX)` and `VisitLocXXXXX(loc::XXXXX)`, where I felt that envoding `NonLoc` and `Loc` in the name is not necessary as the type of the parameter would select the right overload anyways, so I simplified the naming of those visit functions. The rest of the diff is a lot of times just formatting, because `getKind()` by nature, frequently appears in switches, which means that the whole switch gets automatically reformatted. I could probably undo the formatting, but I didn't want to deviate from the rule unless explicitly requested.
1 parent b3eac1a commit bde5717

File tree

9 files changed

+219
-304
lines changed

9 files changed

+219
-304
lines changed

clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ class SValExplainer : public FullSValVisitor<SValExplainer, std::string> {
6565
return "undefined value";
6666
}
6767

68-
std::string VisitLocMemRegionVal(loc::MemRegionVal V) {
68+
std::string VisitMemRegionVal(loc::MemRegionVal V) {
6969
const MemRegion *R = V.getRegion();
7070
// Avoid the weird "pointer to pointee of ...".
7171
if (auto SR = dyn_cast<SymbolicRegion>(R)) {
@@ -76,19 +76,19 @@ class SValExplainer : public FullSValVisitor<SValExplainer, std::string> {
7676
return "pointer to " + Visit(R);
7777
}
7878

79-
std::string VisitLocConcreteInt(loc::ConcreteInt V) {
79+
std::string VisitConcreteInt(loc::ConcreteInt V) {
8080
const llvm::APSInt &I = V.getValue();
8181
std::string Str;
8282
llvm::raw_string_ostream OS(Str);
8383
OS << "concrete memory address '" << I << "'";
8484
return Str;
8585
}
8686

87-
std::string VisitNonLocSymbolVal(nonloc::SymbolVal V) {
87+
std::string VisitSymbolVal(nonloc::SymbolVal V) {
8888
return Visit(V.getSymbol());
8989
}
9090

91-
std::string VisitNonLocConcreteInt(nonloc::ConcreteInt V) {
91+
std::string VisitConcreteInt(nonloc::ConcreteInt V) {
9292
const llvm::APSInt &I = V.getValue();
9393
std::string Str;
9494
llvm::raw_string_ostream OS(Str);
@@ -97,7 +97,7 @@ class SValExplainer : public FullSValVisitor<SValExplainer, std::string> {
9797
return Str;
9898
}
9999

100-
std::string VisitNonLocLazyCompoundVal(nonloc::LazyCompoundVal V) {
100+
std::string VisitLazyCompoundVal(nonloc::LazyCompoundVal V) {
101101
return "lazily frozen compound value of " + Visit(V.getRegion());
102102
}
103103

clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALVISITOR_H
1515
#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALVISITOR_H
1616

17+
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
1718
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
1819
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
19-
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
2020

2121
namespace clang {
2222

@@ -25,49 +25,40 @@ namespace ento {
2525
/// SValVisitor - this class implements a simple visitor for SVal
2626
/// subclasses.
2727
template <typename ImplClass, typename RetTy = void> class SValVisitor {
28-
public:
29-
30-
#define DISPATCH(NAME, CLASS) \
31-
return static_cast<ImplClass *>(this)->Visit ## NAME(V.castAs<CLASS>())
28+
ImplClass &derived() { return *static_cast<ImplClass *>(this); }
3229

30+
public:
3331
RetTy Visit(SVal V) {
3432
// Dispatch to VisitFooVal for each FooVal.
35-
// Take namespaces (loc:: and nonloc::) into account.
36-
switch (V.getBaseKind()) {
37-
#define BASIC_SVAL(Id, Parent) case SVal::Id ## Kind: DISPATCH(Id, Id);
33+
switch (V.getKind()) {
34+
#define BASIC_SVAL(Id, Parent) \
35+
case SVal::Id##Kind: \
36+
return derived().Visit##Id(V.castAs<Id>());
37+
#define LOC_SVAL(Id, Parent) \
38+
case SVal::Loc##Id##Kind: \
39+
return derived().Visit##Id(V.castAs<loc::Id>());
40+
#define NONLOC_SVAL(Id, Parent) \
41+
case SVal::NonLoc##Id##Kind: \
42+
return derived().Visit##Id(V.castAs<nonloc::Id>());
3843
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def"
39-
case SVal::LocKind:
40-
switch (V.getSubKind()) {
41-
#define LOC_SVAL(Id, Parent) \
42-
case loc::Id ## Kind: DISPATCH(Loc ## Id, loc :: Id);
43-
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def"
44-
}
45-
llvm_unreachable("Unknown Loc sub-kind!");
46-
case SVal::NonLocKind:
47-
switch (V.getSubKind()) {
48-
#define NONLOC_SVAL(Id, Parent) \
49-
case nonloc::Id ## Kind: DISPATCH(NonLoc ## Id, nonloc :: Id);
50-
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def"
51-
}
52-
llvm_unreachable("Unknown NonLoc sub-kind!");
5344
}
5445
llvm_unreachable("Unknown SVal kind!");
5546
}
5647

57-
#define BASIC_SVAL(Id, Parent) \
58-
RetTy Visit ## Id(Id V) { DISPATCH(Parent, Id); }
59-
#define ABSTRACT_SVAL(Id, Parent) \
60-
BASIC_SVAL(Id, Parent)
61-
#define LOC_SVAL(Id, Parent) \
62-
RetTy VisitLoc ## Id(loc::Id V) { DISPATCH(Parent, Parent); }
63-
#define NONLOC_SVAL(Id, Parent) \
64-
RetTy VisitNonLoc ## Id(nonloc::Id V) { DISPATCH(Parent, Parent); }
48+
// Dispatch to the more generic handler as a default implementation.
49+
#define BASIC_SVAL(Id, Parent) \
50+
RetTy Visit##Id(Id V) { return derived().Visit##Parent(V.castAs<Id>()); }
51+
#define ABSTRACT_SVAL(Id, Parent) BASIC_SVAL(Id, Parent)
52+
#define LOC_SVAL(Id, Parent) \
53+
RetTy Visit##Id(loc::Id V) { return derived().VisitLoc(V.castAs<Loc>()); }
54+
#define NONLOC_SVAL(Id, Parent) \
55+
RetTy Visit##Id(nonloc::Id V) { \
56+
return derived().VisitNonLoc(V.castAs<NonLoc>()); \
57+
}
6558
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def"
6659

6760
// Base case, ignore it. :)
6861
RetTy VisitSVal(SVal V) { return RetTy(); }
69-
70-
#undef DISPATCH
7162
};
7263

7364
/// SymExprVisitor - this class implements a simple visitor for SymExpr

clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,24 @@
66
//
77
//===----------------------------------------------------------------------===//
88
//
9-
// The list of symbolic values (SVal kinds and sub-kinds) used in the Static
10-
// Analyzer. The distinction between loc:: and nonloc:: SVal namespaces is
9+
// The list of symbolic values (SVal kinds) used in the Static Analyzer.
10+
// The distinction between `loc::` and `nonloc::` SVal namespaces is
1111
// currently hardcoded, because it is too peculiar and explicit to be handled
1212
// uniformly. In order to use this information, users of this file must define
1313
// one or more of the following macros:
1414
//
15-
// BASIC_SVAL(Id, Parent) - for specific SVal sub-kinds, which are
16-
// neither in loc:: nor in nonloc:: namespace; these classes occupy
17-
// their own base kind IdKind.
15+
// BASIC_SVAL(Id, Parent) - for specific SVal kinds, which are
16+
// neither in `loc::` nor in `nonloc::` namespace.
1817
//
1918
// ABSTRACT_SVAL(Id, Parent) - for abstract SVal classes which are
20-
// neither in loc:: nor in nonloc:: namespace,
19+
// neither in `loc::` nor in `nonloc::` namespace,
2120
//
22-
// ABSTRACT_SVAL_WITH_KIND(Id, Parent) - for SVal classes which are also
23-
// neither in loc:: nor in nonloc:: namespace, but occupy a whole base kind
24-
// identifier IdKind, much like BASIC_SVALs.
21+
// LOC_SVAL(Id, Parent) - for values in `loc::` namespace.
2522
//
26-
// LOC_SVAL(Id, Parent) - for values in loc:: namespace, which occupy a sub-kind
27-
// loc::IdKind.
23+
// NONLOC_SVAL(Id, Parent) - for values in `nonloc::` namespace.
2824
//
29-
// NONLOC_SVAL(Id, Parent) - for values in nonloc:: namespace, which occupy a
30-
// sub-kind nonloc::IdKind.
25+
// SVAL_RANGE(Id, First, Last) - for defining range of subtypes of
26+
// the abstract class `Id`.
3127
//
3228
//===----------------------------------------------------------------------===//
3329

@@ -39,10 +35,6 @@
3935
#define ABSTRACT_SVAL(Id, Parent)
4036
#endif
4137

42-
#ifndef ABSTRACT_SVAL_WITH_KIND
43-
#define ABSTRACT_SVAL_WITH_KIND(Id, Parent) ABSTRACT_SVAL(Id, Parent)
44-
#endif
45-
4638
#ifndef LOC_SVAL
4739
#define LOC_SVAL(Id, Parent)
4840
#endif
@@ -51,24 +43,30 @@
5143
#define NONLOC_SVAL(Id, Parent)
5244
#endif
5345

46+
#ifndef SVAL_RANGE
47+
#define SVAL_RANGE(Id, First, Last)
48+
#endif
49+
5450
BASIC_SVAL(UndefinedVal, SVal)
5551
ABSTRACT_SVAL(DefinedOrUnknownSVal, SVal)
5652
BASIC_SVAL(UnknownVal, DefinedOrUnknownSVal)
5753
ABSTRACT_SVAL(DefinedSVal, DefinedOrUnknownSVal)
58-
ABSTRACT_SVAL_WITH_KIND(Loc, DefinedSVal)
54+
ABSTRACT_SVAL(Loc, DefinedSVal)
5955
LOC_SVAL(ConcreteInt, Loc)
6056
LOC_SVAL(GotoLabel, Loc)
6157
LOC_SVAL(MemRegionVal, Loc)
62-
ABSTRACT_SVAL_WITH_KIND(NonLoc, DefinedSVal)
58+
SVAL_RANGE(Loc, ConcreteInt, MemRegionVal)
59+
ABSTRACT_SVAL(NonLoc, DefinedSVal)
6360
NONLOC_SVAL(CompoundVal, NonLoc)
6461
NONLOC_SVAL(ConcreteInt, NonLoc)
6562
NONLOC_SVAL(LazyCompoundVal, NonLoc)
6663
NONLOC_SVAL(LocAsInteger, NonLoc)
6764
NONLOC_SVAL(SymbolVal, NonLoc)
6865
NONLOC_SVAL(PointerToMember, NonLoc)
66+
SVAL_RANGE(NonLoc, CompoundVal, PointerToMember)
6967

68+
#undef SVAL_RANGE
7069
#undef NONLOC_SVAL
7170
#undef LOC_SVAL
72-
#undef ABSTRACT_SVAL_WITH_KIND
7371
#undef ABSTRACT_SVAL
7472
#undef BASIC_SVAL

0 commit comments

Comments
 (0)