Skip to content

Commit 548ad57

Browse files
committed
[analyzer] Don't copy field-by-field conjured LazyCompoundVals (2/4)
1 parent 9685681 commit 548ad57

File tree

3 files changed

+45
-7
lines changed

3 files changed

+45
-7
lines changed

clang/lib/StaticAnalyzer/Core/RegionStore.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,12 @@ class RegionStoreManager : public StoreManager {
608608
return getBinding(getRegionBindings(S), L, T);
609609
}
610610

611+
/// Returns the value of the default binding of region \p BaseR
612+
/// if and only if that is the unique binding in the cluster of \p BaseR.
613+
/// \p BaseR must be a base region.
614+
std::optional<SVal> getUniqueDefaultBinding(Store S,
615+
const MemRegion *BaseR) const;
616+
611617
std::optional<SVal> getDefaultBinding(Store S, const MemRegion *R) override {
612618
RegionBindingsRef B = getRegionBindings(S);
613619
// Default bindings are always applied over a base region so look up the
@@ -2605,9 +2611,42 @@ RegionBindingsRef RegionStoreManager::bindVector(RegionBindingsConstRef B,
26052611
return NewB;
26062612
}
26072613

2614+
std::optional<SVal>
2615+
RegionStoreManager::getUniqueDefaultBinding(Store S,
2616+
const MemRegion *BaseR) const {
2617+
assert(BaseR == BaseR->getBaseRegion() && "Expecting a base region");
2618+
const auto *Cluster = getRegionBindings(S).lookup(BaseR);
2619+
if (!Cluster || !llvm::hasSingleElement(*Cluster))
2620+
return std::nullopt;
2621+
2622+
const auto [Key, Value] = *Cluster->begin();
2623+
return Key.isDirect() ? std::optional<SVal>{} : Value;
2624+
}
2625+
26082626
std::optional<RegionBindingsRef> RegionStoreManager::tryBindSmallStruct(
26092627
RegionBindingsConstRef B, const TypedValueRegion *R, const RecordDecl *RD,
26102628
nonloc::LazyCompoundVal LCV) {
2629+
// If we try to copy a Conjured value representing the value of the whole
2630+
// struct, don't try to element-wise copy each field.
2631+
// That would unnecessarily bind Derived symbols slicing off the subregion for
2632+
// the field from the whole Conjured symbol.
2633+
//
2634+
// struct Window { int width; int height; };
2635+
// Window getWindow(); <-- opaque fn.
2636+
// Window w = getWindow(); <-- conjures a new Window.
2637+
// Window w2 = w; <-- trivial copy "w", calling "tryBindSmallStruct"
2638+
//
2639+
// We should not end up with a new Store for "w2" like this:
2640+
// Direct [ 0..31]: Derived{Conj{}, w.width}
2641+
// Direct [32..63]: Derived{Conj{}, w.height}
2642+
// Instead, we should just bind that Conjured value instead.
2643+
if (LCV.getRegion()->getBaseRegion() == LCV.getRegion()) {
2644+
if (auto Val = getUniqueDefaultBinding(LCV.getStore(), LCV.getRegion())) {
2645+
return B.addBinding(BindingKey::Make(R, BindingKey::Default),
2646+
Val.value());
2647+
}
2648+
}
2649+
26112650
FieldVector Fields;
26122651

26132652
if (const CXXRecordDecl *Class = dyn_cast<CXXRecordDecl>(RD))

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,9 @@ void _02_structs_with_members() {
8383
clang_analyzer_dump_val(Aggr2); // expected-warning {{lazyCompoundVal}}
8484
clang_analyzer_dump_val(Aggr3); // expected-warning {{lazyCompoundVal}}
8585

86-
// We have fields in the struct we copy, thus we also have the entries for the copies
87-
// (and for all of their fields).
86+
// We should have the same Conjured symbol for "Aggr", "Aggr2" and "Aggr3".
87+
// We used to have Derived symbols for the individual fields that were
88+
// copied as part of copying the whole struct.
8889
clang_analyzer_printState();
8990
// CHECK: "store": { "pointer": "0x{{[0-9a-f]+}}", "items": [
9091
// CHECK-NEXT: { "cluster": "GlobalInternalSpaceRegion", "pointer": "0x{{[0-9a-f]+}}", "items": [
@@ -97,12 +98,10 @@ void _02_structs_with_members() {
9798
// CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "[[AGGR_CONJ:conj_\$[0-9]+{int, LC[0-9]+, S[0-9]+, #[0-9]+}]]" }
9899
// CHECK-NEXT: ]},
99100
// CHECK-NEXT: { "cluster": "Aggr2", "pointer": "0x{{[0-9a-f]+}}", "items": [
100-
// CHECK-NEXT: { "kind": "Direct", "offset": 0, "value": "derived_${{[0-9]+}}{[[AGGR_CONJ]],Aggr.x}" },
101-
// CHECK-NEXT: { "kind": "Direct", "offset": 32, "value": "derived_${{[0-9]+}}{[[AGGR_CONJ]],Aggr.y}" }
101+
// CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "[[AGGR_CONJ]]" }
102102
// CHECK-NEXT: ]},
103103
// CHECK-NEXT: { "cluster": "Aggr3", "pointer": "0x{{[0-9a-f]+}}", "items": [
104-
// CHECK-NEXT: { "kind": "Direct", "offset": 0, "value": "derived_${{[0-9]+}}{[[AGGR_CONJ]],Aggr.x}" },
105-
// CHECK-NEXT: { "kind": "Direct", "offset": 32, "value": "derived_${{[0-9]+}}{[[AGGR_CONJ]],Aggr.y}" }
104+
// CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "[[AGGR_CONJ]]" }
106105
// CHECK-NEXT: ]}
107106
// CHECK-NEXT: ]},
108107

clang/test/Analysis/store-dump-orders.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ void test_output(int n) {
4141
// CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "conj_$
4242
// CHECK-NEXT: ]},
4343
// CHECK-NEXT: { "cluster": "objfirst", "pointer": "0x{{[0-9a-f]+}}", "items": [
44-
// CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "lazyCompoundVal
44+
// CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "conj_$
4545
// CHECK-NEXT: { "kind": "Direct", "offset": 320, "value": "1 S32b" },
4646
// CHECK-NEXT: { "kind": "Direct", "offset": 352, "value": "2 S32b" },
4747
// CHECK-NEXT: { "kind": "Direct", "offset": 384, "value": "3 S32b" }

0 commit comments

Comments
 (0)