55void clang_analyzer_printState ();
66template <typename T> void clang_analyzer_dump_lref (T& param);
77template <typename T> void clang_analyzer_dump_val (T param);
8- template <typename T> void clang_analyzer_denote (T param, const char *name);
9- template <typename T> void clang_analyzer_express (T param);
108template <typename T> T conjure ();
119template <typename ... Ts> void nop (const Ts &... args) {}
1210
@@ -42,10 +40,16 @@ void test_assign_return() {
4240namespace trivial_struct_copy {
4341
4442void _01_empty_structs () {
45- clang_analyzer_dump_val (conjure<empty>()); // expected-warning {{conj_$ }}
43+ clang_analyzer_dump_val (conjure<empty>()); // expected-warning {{lazyCompoundVal }}
4644 empty Empty = conjure<empty>();
4745 empty Empty2 = Empty;
4846 empty Empty3 = Empty2;
47+ // All of these should refer to the exact same LCV, because all of
48+ // these trivial copies refer to the original conjured value.
49+ // There were Unknown before:
50+ clang_analyzer_dump_val (Empty); // expected-warning {{lazyCompoundVal}}
51+ clang_analyzer_dump_val (Empty2); // expected-warning {{lazyCompoundVal}}
52+ clang_analyzer_dump_val (Empty3); // expected-warning {{lazyCompoundVal}}
4953
5054 // We only have binding for the original Empty object, because copying empty
5155 // objects is a no-op in the performTrivialCopy. This is fine, because empty
@@ -67,20 +71,18 @@ void _01_empty_structs() {
6771}
6872
6973void _02_structs_with_members () {
70- clang_analyzer_dump_val (conjure<aggr>()); // expected-warning {{conj_$ }}
74+ clang_analyzer_dump_val (conjure<aggr>()); // expected-warning {{lazyCompoundVal }}
7175 aggr Aggr = conjure<aggr>();
7276 aggr Aggr2 = Aggr;
7377 aggr Aggr3 = Aggr2;
74- // All of these should refer to the exact same symbol , because all of
78+ // All of these should refer to the exact same LCV , because all of
7579 // these trivial copies refer to the original conjured value.
76- clang_analyzer_denote (Aggr, " $Aggr" );
77- clang_analyzer_express (Aggr); // expected-warning {{$Aggr}}
78- clang_analyzer_express (Aggr2); // expected-warning {{$Aggr}}
79- clang_analyzer_express (Aggr3); // expected-warning {{$Aggr}}
80-
81- // We should have the same Conjured symbol for "Aggr", "Aggr2" and "Aggr3".
82- // We used to have Derived symbols for the individual fields that were
83- // copied as part of copying the whole struct.
80+ clang_analyzer_dump_val (Aggr); // expected-warning {{lazyCompoundVal}}
81+ clang_analyzer_dump_val (Aggr2); // expected-warning {{lazyCompoundVal}}
82+ clang_analyzer_dump_val (Aggr3); // expected-warning {{lazyCompoundVal}}
83+
84+ // We have fields in the struct we copy, thus we also have the entries for the copies
85+ // (and for all of their fields).
8486 clang_analyzer_printState ();
8587 // CHECK: "store": { "pointer": "0x{{[0-9a-f]+}}", "items": [
8688 // CHECK-NEXT: { "cluster": "GlobalInternalSpaceRegion", "pointer": "0x{{[0-9a-f]+}}", "items": [
@@ -93,10 +95,12 @@ void _02_structs_with_members() {
9395 // CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "[[AGGR_CONJ:conj_\$[0-9]+{int, LC[0-9]+, S[0-9]+, #[0-9]+}]]" }
9496 // CHECK-NEXT: ]},
9597 // CHECK-NEXT: { "cluster": "Aggr2", "pointer": "0x{{[0-9a-f]+}}", "items": [
96- // CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "[[AGGR_CONJ]]" }
98+ // CHECK-NEXT: { "kind": "Direct", "offset": 0, "value": "derived_${{[0-9]+}}{[[AGGR_CONJ]],Aggr.x}" },
99+ // CHECK-NEXT: { "kind": "Direct", "offset": 32, "value": "derived_${{[0-9]+}}{[[AGGR_CONJ]],Aggr.y}" }
97100 // CHECK-NEXT: ]},
98101 // CHECK-NEXT: { "cluster": "Aggr3", "pointer": "0x{{[0-9a-f]+}}", "items": [
99- // CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "[[AGGR_CONJ]]" }
102+ // CHECK-NEXT: { "kind": "Direct", "offset": 0, "value": "derived_${{[0-9]+}}{[[AGGR_CONJ]],Aggr.x}" },
103+ // CHECK-NEXT: { "kind": "Direct", "offset": 32, "value": "derived_${{[0-9]+}}{[[AGGR_CONJ]],Aggr.y}" }
100104 // CHECK-NEXT: ]}
101105 // CHECK-NEXT: ]},
102106
@@ -113,3 +117,31 @@ void entrypoint() {
113117}
114118
115119} // namespace trivial_struct_copy
120+
121+ namespace gh153782 {
122+
123+ // Ensure we do not regress on the following use cases.
124+ // The assumption made on a field in `setPtr` should apply to the returned copy in `func`.
125+ struct Status { int error; };
126+ Status getError ();
127+
128+ Status setPtr (int **outptr, int * ptr) {
129+ Status e = getError ();
130+ if (e.error != 0 ) return e; // When assuming the error field is non-zero,
131+ *outptr = ptr; // this is not executed
132+ return e;
133+ }
134+
135+ int func () {
136+ int *ptr = nullptr ;
137+ int x = 42 ;
138+ if (setPtr (&ptr, &x).error == 0 ) {
139+ // The assumption made in get() SHOULD match the assumption about
140+ // the returned value, hence the engine SHOULD NOT assume ptr is null.
141+ clang_analyzer_dump_val (ptr); // expected-warning {{&x}}
142+ return *ptr;
143+ }
144+ return 0 ;
145+ }
146+
147+ } // namespace gh153782
0 commit comments