Skip to content

Commit 0dfae06

Browse files
authored
[analyzer] Trigger copy event when copying empty structs (3/4) (llvm#115918)
Previously, ExprEngine would just skip copying empty structs. Let's make trigger the copy event even for empty structs. Split from llvm#114835
1 parent 9122c52 commit 0dfae06

File tree

2 files changed

+16
-22
lines changed

2 files changed

+16
-22
lines changed

clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -68,23 +68,15 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred,
6868
Bldr.takeNodes(Pred);
6969

7070
assert(ThisRD);
71-
if (!ThisRD->isEmpty()) {
72-
// Load the source value only for non-empty classes.
73-
// Otherwise it'd retrieve an UnknownVal
74-
// and bind it and RegionStore would think that the actual value
75-
// in this region at this offset is unknown.
76-
SVal V = Call.getArgSVal(0);
77-
78-
// If the value being copied is not unknown, load from its location to get
79-
// an aggregate rvalue.
80-
if (std::optional<Loc> L = V.getAs<Loc>())
81-
V = Pred->getState()->getSVal(*L);
82-
else
83-
assert(V.isUnknownOrUndef());
84-
evalBind(Dst, CallExpr, Pred, ThisVal, V, true);
85-
} else {
86-
Dst.Add(Pred);
87-
}
71+
SVal V = Call.getArgSVal(0);
72+
73+
// If the value being copied is not unknown, load from its location to get
74+
// an aggregate rvalue.
75+
if (std::optional<Loc> L = V.getAs<Loc>())
76+
V = Pred->getState()->getSVal(*L);
77+
else
78+
assert(V.isUnknownOrUndef());
79+
evalBind(Dst, CallExpr, Pred, ThisVal, V, true);
8880

8981
PostStmt PS(CallExpr, LCtx);
9082
for (ExplodedNode *N : Dst) {

clang/test/Analysis/ctor-trivial-copy.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,7 @@ void _01_empty_structs() {
5151
clang_analyzer_dump_val(Empty2); // expected-warning {{lazyCompoundVal}}
5252
clang_analyzer_dump_val(Empty3); // expected-warning {{lazyCompoundVal}}
5353

54-
// Notice that we don't have entries for the copies, only for the original "Empty" object.
55-
// That binding was added by the opaque "conjure" call, directly constructing to the variable "Empty" by copy-elision.
56-
// The copies are not present because the ExprEngine skips the evalBind of empty structs.
57-
// And even if such binds would reach the Store, the Store copies small structs by copying the individual fields,
58-
// and there are no fields within "Empty", thus we wouldn't have any entries anyways.
54+
// We should have the same Conjured symbol for "Empty", "Empty2" and "Empty3".
5955
clang_analyzer_printState();
6056
// CHECK: "store": { "pointer": "0x{{[0-9a-f]+}}", "items": [
6157
// CHECK-NEXT: { "cluster": "GlobalInternalSpaceRegion", "pointer": "0x{{[0-9a-f]+}}", "items": [
@@ -66,6 +62,12 @@ void _01_empty_structs() {
6662
// CHECK-NEXT: ]},
6763
// CHECK-NEXT: { "cluster": "Empty", "pointer": "0x{{[0-9a-f]+}}", "items": [
6864
// CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "[[EMPTY_CONJ:conj_\$[0-9]+{int, LC[0-9]+, S[0-9]+, #[0-9]+}]]" }
65+
// CHECK-NEXT: ]},
66+
// CHECK-NEXT: { "cluster": "Empty2", "pointer": "0x{{[0-9a-f]+}}", "items": [
67+
// CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "[[EMPTY_CONJ]]" }
68+
// CHECK-NEXT: ]},
69+
// CHECK-NEXT: { "cluster": "Empty3", "pointer": "0x{{[0-9a-f]+}}", "items": [
70+
// CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "[[EMPTY_CONJ]]" }
6971
// CHECK-NEXT: ]}
7072
// CHECK-NEXT: ]},
7173

0 commit comments

Comments
 (0)