xref: /llvm-project/clang/docs/analyzer/developer-docs/InitializerLists.rst (revision 0c660256eb41fb0ba44277a32f39d2a028f797f2)
1*1a17032bSKristof Umann================
2*1a17032bSKristof UmannInitializer List
3*1a17032bSKristof Umann================
4*1a17032bSKristof UmannThis discussion took place in https://reviews.llvm.org/D35216
5*1a17032bSKristof Umann"Escape symbols when creating std::initializer_list".
6*1a17032bSKristof Umann
7*1a17032bSKristof UmannIt touches problems of modelling C++ standard library constructs in general,
8*1a17032bSKristof Umannincluding modelling implementation-defined fields within C++ standard library
9*1a17032bSKristof Umannobjects, in particular constructing objects into pointers held by such fields,
10*1a17032bSKristof Umannand separation of responsibilities between analyzer's core and checkers.
11*1a17032bSKristof Umann
12*1a17032bSKristof Umann**Artem:**
13*1a17032bSKristof Umann
14*1a17032bSKristof UmannI've seen a few false positives that appear because we construct
15*1a17032bSKristof UmannC++11 std::initializer_list objects with brace initializers, and such
16*1a17032bSKristof Umannconstruction is not properly modeled. For instance, if a new object is
17*1a17032bSKristof Umannconstructed on the heap only to be put into a brace-initialized STL container,
18*1a17032bSKristof Umannthe object is reported to be leaked.
19*1a17032bSKristof Umann
20*1a17032bSKristof UmannApproach (0): This can be trivially fixed by this patch, which causes pointers
21*1a17032bSKristof Umannpassed into initializer list expressions to immediately escape.
22*1a17032bSKristof Umann
23*1a17032bSKristof UmannThis fix is overly conservative though. So i did a bit of investigation as to
24*1a17032bSKristof Umannhow model std::initializer_list better.
25*1a17032bSKristof Umann
26*1a17032bSKristof UmannAccording to the standard, ``std::initializer_list<T>`` is an object that has
27*1a17032bSKristof Umannmethods ``begin(), end(), and size()``, where ``begin()`` returns a pointer to continuous
28*1a17032bSKristof Umannarray of ``size()`` objects of type T, and end() is equal to begin() plus size().
29*1a17032bSKristof UmannThe standard does hint that it should be possible to implement
30*1a17032bSKristof Umann``std::initializer_list<T>`` as a pair of pointers, or as a pointer and a size
31*1a17032bSKristof Umanninteger, however specific fields that the object would contain are an
32*1a17032bSKristof Umannimplementation detail.
33*1a17032bSKristof Umann
34*1a17032bSKristof UmannIdeally, we should be able to model the initializer list's methods precisely.
35*1a17032bSKristof UmannOr, at least, it should be possible to explain to the analyzer that the list
36*1a17032bSKristof Umannsomehow "takes hold" of the values put into it. Initializer lists can also be
37*1a17032bSKristof Umanncopied, which is a separate story that i'm not trying to address here.
38*1a17032bSKristof Umann
39*1a17032bSKristof UmannThe obvious approach to modeling ``std::initializer_list`` in a checker would be to
40*1a17032bSKristof Umannconstruct a SymbolMetadata for the memory region of the initializer list object,
41*1a17032bSKristof Umannwhich would be of type ``T*`` and represent ``begin()``, so we'd trivially model ``begin()``
42*1a17032bSKristof Umannas a function that returns this symbol. The array pointed to by that symbol
43*1a17032bSKristof Umannwould be ``bindLoc()``ed to contain the list's contents (probably as a ``CompoundVal``
44*1a17032bSKristof Umannto produce less bindings in the store). Extent of this array would represent
45*1a17032bSKristof Umann``size()`` and would be equal to the length of the list as written.
46*1a17032bSKristof Umann
47*1a17032bSKristof UmannSo this sounds good, however apparently it does nothing to address our false
48*1a17032bSKristof Umannpositives: when the list escapes, our ``RegionStoreManager`` is not magically
49*1a17032bSKristof Umannguessing that the metadata symbol attached to it, together with its contents,
50*1a17032bSKristof Umannshould also escape. In fact, it's impossible to trigger a pointer escape from
51*1a17032bSKristof Umannwithin the checker.
52*1a17032bSKristof Umann
53*1a17032bSKristof UmannApproach (1): If only we enabled ``ProgramState::bindLoc(..., notifyChanges=true)``
54*1a17032bSKristof Umannto cause pointer escapes (not only region changes) (which sounds like the right
55*1a17032bSKristof Umannthing to do anyway) such checker would be able to solve the false positives by
56*1a17032bSKristof Umanntriggering escapes when binding list elements to the list. However, it'd be as
57*1a17032bSKristof Umannconservative as the current patch's solution. Ideally, we do not want escapes to
58*1a17032bSKristof Umannhappen so early. Instead, we'd prefer them to be delayed until the list itself
59*1a17032bSKristof Umannescapes.
60*1a17032bSKristof Umann
61*1a17032bSKristof UmannSo i believe that escaping metadata symbols whenever their base regions escape
62*1a17032bSKristof Umannwould be the right thing to do. Currently we didn't think about that because we
63*1a17032bSKristof Umannhad neither pointer-type metadatas nor non-pointer escapes.
64*1a17032bSKristof Umann
65*1a17032bSKristof UmannApproach (2): We could teach the Store to scan itself for bindings to
66*1a17032bSKristof Umannmetadata-symbolic-based regions during scanReachableSymbols() whenever a region
67*1a17032bSKristof Umannturns out to be reachable. This requires no work on checker side, but it sounds
68*1a17032bSKristof Umannperformance-heavy.
69*1a17032bSKristof Umann
70*1a17032bSKristof UmannApproach (3): We could let checkers maintain the set of active metadata symbols
71*1a17032bSKristof Umannin the program state (ideally somewhere in the Store, which sounds weird but
72*1a17032bSKristof Umanncauses the smallest amount of layering violations), so that the core knew what
73*1a17032bSKristof Umannto escape. This puts a stress on the checkers, but with a smart data map it
74*1a17032bSKristof Umannwouldn't be a problem.
75*1a17032bSKristof Umann
76*1a17032bSKristof UmannApproach (4): We could allow checkers to trigger pointer escapes in arbitrary
77*1a17032bSKristof Umannmoments. If we allow doing this within ``checkPointerEscape`` callback itself, we
78*1a17032bSKristof Umannwould be able to express facts like "when this region escapes, that metadata
79*1a17032bSKristof Umannsymbol attached to it should also escape". This sounds like an ultimate freedom,
80*1a17032bSKristof Umannwith maximum stress on the checkers - still not too much stress when we have
81*1a17032bSKristof Umannsmart data maps.
82*1a17032bSKristof Umann
83*1a17032bSKristof UmannI'm personally liking the approach (2) - it should be possible to avoid
84*1a17032bSKristof Umannperformance overhead, and clarity seems nice.
85*1a17032bSKristof Umann
86*1a17032bSKristof Umann**Gabor:**
87*1a17032bSKristof Umann
88*1a17032bSKristof UmannAt this point, I am a bit wondering about two questions.
89*1a17032bSKristof Umann
90*1a17032bSKristof Umann* When should something belong to a checker and when should something belong to the engine?
91*1a17032bSKristof Umann  Sometimes we model library aspects in the engine and model language constructs in checkers.
92*1a17032bSKristof Umann
93*1a17032bSKristof Umann* What is the checker programming model that we are aiming for? Maximum freedom or more easy checker development?
94*1a17032bSKristof Umann
95*1a17032bSKristof UmannI think if we aim for maximum freedom, we do not need to worry about the
96*1a17032bSKristof Umannpotential stress on checkers, and we can introduce abstractions to mitigate that
97*1a17032bSKristof Umannlater on.
98*1a17032bSKristof UmannIf we want to simplify the API, then maybe it makes more sense to move language
99*1a17032bSKristof Umannconstruct modeling to the engine when the checker API is not sufficient instead
100*1a17032bSKristof Umannof complicating the API.
101*1a17032bSKristof Umann
102*1a17032bSKristof UmannRight now I have no preference or objections between the alternatives but there
103*1a17032bSKristof Umannare some random thoughts:
104*1a17032bSKristof Umann
105*1a17032bSKristof Umann* Maybe it would be great to have a guideline how to evolve the analyzer and
106*1a17032bSKristof Umann  follow it, so it can help us to decide in similar situations
107*1a17032bSKristof Umann
108*1a17032bSKristof Umann* I do care about performance in this case. The reason is that we have a
109*1a17032bSKristof Umann  limited performance budget. And I think we should not expect most of the checker
110*1a17032bSKristof Umann  writers to add modeling of language constructs. So, in my opinion, it is ok to
111*1a17032bSKristof Umann  have less nice/more verbose API for language modeling if we can have better
112*1a17032bSKristof Umann  performance this way, since it only needs to be done once, and is done by the
113*1a17032bSKristof Umann  framework developers.
114*1a17032bSKristof Umann
115*1a17032bSKristof Umann**Artem:** These are some great questions, i guess it'd be better to discuss
116*1a17032bSKristof Umannthem more openly. As a quick dump of my current mood:
117*1a17032bSKristof Umann
118*1a17032bSKristof Umann* To me it seems obvious that we need to aim for a checker API that is both
119*1a17032bSKristof Umann  simple and powerful. This can probably by keeping the API as powerful as
120*1a17032bSKristof Umann  necessary while providing a layer of simple ready-made solutions on top of it.
121*1a17032bSKristof Umann  Probably a few reusable components for assembling checkers. And this layer
122*1a17032bSKristof Umann  should ideally be pleasant enough to work with, so that people would prefer to
123*1a17032bSKristof Umann  extend it when something is lacking, instead of falling back to the complex
124*1a17032bSKristof Umann  omnipotent API. I'm thinking of AST matchers vs. AST visitors as a roughly
125*1a17032bSKristof Umann  similar situation: matchers are not omnipotent, but they're so nice.
126*1a17032bSKristof Umann
127*1a17032bSKristof Umann* Separation between core and checkers is usually quite strange. Once we have
128*1a17032bSKristof Umann  shared state traits, i generally wouldn't mind having region store or range
129*1a17032bSKristof Umann  constraint manager as checkers (though it's probably not worth it to transform
130*1a17032bSKristof Umann  them - just a mood). The main thing to avoid here would be the situation when
131*1a17032bSKristof Umann  the checker overwrites stuff written by the core because it thinks it has a
132*1a17032bSKristof Umann  better idea what's going on, so the core should provide a good default behavior.
133*1a17032bSKristof Umann
134*1a17032bSKristof Umann* Yeah, i totally care about performance as well, and if i try to implement
135*1a17032bSKristof Umann  approach, i'd make sure it's good.
136*1a17032bSKristof Umann
137*1a17032bSKristof Umann**Artem:**
138*1a17032bSKristof Umann
139*1a17032bSKristof Umann> Approach (2): We could teach the Store to scan itself for bindings to
140*1a17032bSKristof Umann> metadata-symbolic-based regions during scanReachableSymbols() whenever
141*1a17032bSKristof Umann> a region turns out to be reachable. This requires no work on checker side,
142*1a17032bSKristof Umann> but it sounds performance-heavy.
143*1a17032bSKristof Umann
144*1a17032bSKristof UmannNope, this approach is wrong. Metadata symbols may become out-of-date: when the
145*1a17032bSKristof Umannobject changes, metadata symbols attached to it aren't changing (because symbols
146*1a17032bSKristof Umannsimply don't change). The same metadata may have different symbols to denote its
147*1a17032bSKristof Umannvalue in different moments of time, but at most one of them represents the
148*1a17032bSKristof Umannactual metadata value. So we'd be escaping more stuff than necessary.
149*1a17032bSKristof Umann
150*1a17032bSKristof UmannIf only we had "ghost fields"
151*1a17032bSKristof Umann(https://lists.llvm.org/pipermail/cfe-dev/2016-May/049000.html), it would have
152*1a17032bSKristof Umannbeen much easier, because the ghost field would only contain the actual
153*1a17032bSKristof Umannmetadata, and the Store would always know about it. This example adds to my
154*1a17032bSKristof Umannbelief that ghost fields are exactly what we need for most C++ checkers.
155*1a17032bSKristof Umann
156*1a17032bSKristof Umann**Devin:**
157*1a17032bSKristof Umann
158*1a17032bSKristof UmannIn this case, I would be fine with some sort of
159*1a17032bSKristof UmannAbstractStorageMemoryRegion that meant "here is a memory region and somewhere
160*1a17032bSKristof Umannreachable from here exists another region of type T". Or even multiple regions
161*1a17032bSKristof Umannwith different identifiers. This wouldn't specify how the memory is reachable,
162*1a17032bSKristof Umannbut it would allow for transfer functions to get at those regions and it would
163*1a17032bSKristof Umannallow for invalidation.
164*1a17032bSKristof Umann
165*1a17032bSKristof UmannFor ``std::initializer_list`` this reachable region would the region for the backing
166*1a17032bSKristof Umannarray and the transfer functions for begin() and end() yield the beginning and
167*1a17032bSKristof Umannend element regions for it.
168*1a17032bSKristof Umann
169*1a17032bSKristof UmannIn my view this differs from ghost variables in that (1) this storage does
170*1a17032bSKristof Umannactually exist (it is just a library implementation detail where that storage
171*1a17032bSKristof Umannlives) and (2) it is perfectly valid for a pointer into that storage to be
172*1a17032bSKristof Umannreturned and for another part of the program to read or write from that storage.
173*1a17032bSKristof Umann(Well, in this case just read since it is allowed to be read-only memory).
174*1a17032bSKristof Umann
175*1a17032bSKristof UmannWhat I'm not OK with is modeling abstract analysis state (for example, the count
176*1a17032bSKristof Umannof a NSMutableArray or the typestate of a file handle) as a value stored in some
177*1a17032bSKristof Umannginned up region in the store. This takes an easy problem that the analyzer does
178*1a17032bSKristof Umannwell at (modeling typestate) and turns it into a hard one that the analyzer is
179*1a17032bSKristof Umannbad at (reasoning about the contents of the heap).
180*1a17032bSKristof Umann
181*1a17032bSKristof UmannI think the key criterion here is: "is the region accessible from outside the
182*1a17032bSKristof Umannlibrary". That is, does the library expose the region as a pointer that can be
183*1a17032bSKristof Umannread to or written from in the client program? If so, then it makes sense for
184*1a17032bSKristof Umannthis to be in the store: we are modeling reachable storage as storage. But if
185*1a17032bSKristof Umannwe're just modeling arbitrary analysis facts that need to be invalidated when a
186*1a17032bSKristof Umannpointer escapes then we shouldn't try to gin up storage for them just to get
187*1a17032bSKristof Umanninvalidation for free.
188*1a17032bSKristof Umann
189*1a17032bSKristof Umann**Artem:**
190*1a17032bSKristof Umann
191*1a17032bSKristof Umann> In this case, I would be fine with some sort of ``AbstractStorageMemoryRegion``
192*1a17032bSKristof Umann> that meant "here is a memory region and somewhere reachable from here exists
193*1a17032bSKristof Umann> another region of type T". Or even multiple regions with different
194*1a17032bSKristof Umann> identifiers. This wouldn't specify how the memory is reachable, but it would
195*1a17032bSKristof Umann> allow for transfer functions to get at those regions and it would allow for
196*1a17032bSKristof Umann> invalidation.
197*1a17032bSKristof Umann
198*1a17032bSKristof UmannYeah, this is what we can easily implement now as a
199*1a17032bSKristof Umannsymbolic-region-based-on-a-metadata-symbol (though we can make a new region
200*1a17032bSKristof Umannclass for that if we eg. want it typed). The problem is that the relation
201*1a17032bSKristof Umannbetween such storage region and its parent object region is essentially
202*1a17032bSKristof Umannimmaterial, similarly to the relation between ``SymbolRegionValue`` and its parent
203*1a17032bSKristof Umannregion. Region contents are mutable: today the abstract storage is reachable
204*1a17032bSKristof Umannfrom its parent object, tomorrow it's not, and maybe something else becomes
205*1a17032bSKristof Umannreachable, something that isn't even abstract. So the parent region for the
206*1a17032bSKristof Umannabstract storage is most of the time at best a "nice to know" thing - we cannot
207*1a17032bSKristof Umannrely on it to do any actual work. We'd anyway need to rely on the checker to do
208*1a17032bSKristof Umannthe job.
209*1a17032bSKristof Umann
210*1a17032bSKristof Umann> For std::initializer_list this reachable region would the region for the
211*1a17032bSKristof Umann> backing array and the transfer functions for begin() and end() yield the
212*1a17032bSKristof Umann> beginning and end element regions for it.
213*1a17032bSKristof Umann
214*1a17032bSKristof UmannSo maybe in fact for std::initializer_list it may work fine because you cannot
215*1a17032bSKristof Umannchange the data after the object is constructed - so this region's contents are
216*1a17032bSKristof Umannessentially immutable. For the future, i feel as if it is a dead end.
217*1a17032bSKristof Umann
218*1a17032bSKristof UmannI'd like to consider another funny example. Suppose we're trying to model
219*1a17032bSKristof Umann
220*1a17032bSKristof Umann.. code-block:: cpp
221*1a17032bSKristof Umann
222*1a17032bSKristof Umann std::unique_ptr. Consider::
223*1a17032bSKristof Umann
224*1a17032bSKristof Umann   void bar(const std::unique_ptr<int> &x);
225*1a17032bSKristof Umann
226*1a17032bSKristof Umann   void foo(std::unique_ptr<int> &x) {
227*1a17032bSKristof Umann     int *a = x.get();   // (a, 0, direct): &AbstractStorageRegion
228*1a17032bSKristof Umann     *a = 1;             // (AbstractStorageRegion, 0, direct): 1 S32b
229*1a17032bSKristof Umann     int *b = new int;
230*1a17032bSKristof Umann     *b = 2;             // (SymRegion{conj_$0<int *>}, 0 ,direct): 2 S32b
231*1a17032bSKristof Umann     x.reset(b);         // Checker map: x -> SymRegion{conj_$0<int *>}
232*1a17032bSKristof Umann     bar(x);             // 'a' doesn't escape (the pointer was unique), 'b' does.
233*1a17032bSKristof Umann     clang_analyzer_eval(*a == 1); // Making this true is up to the checker.
234*1a17032bSKristof Umann     clang_analyzer_eval(*b == 2); // Making this unknown is up to the checker.
235*1a17032bSKristof Umann   }
236*1a17032bSKristof Umann
237*1a17032bSKristof UmannThe checker doesn't totally need to ensure that ``*a == 1`` passes - even though the
238*1a17032bSKristof Umannpointer was unique, it could theoretically have ``.get()``-ed above and the code
239*1a17032bSKristof Umanncould of course break the uniqueness invariant (though we'd probably want it).
240*1a17032bSKristof UmannThe checker can say that "even if ``*a`` did escape, it was not because it was
241*1a17032bSKristof Umannstuffed directly into bar()".
242*1a17032bSKristof Umann
243*1a17032bSKristof UmannThe checker's direct responsibility, however, is to solve the ``*b == 2`` thing
244*1a17032bSKristof Umann(which is in fact the problem we're dealing with in this patch - escaping the
245*1a17032bSKristof Umannstorage region of the object).
246*1a17032bSKristof Umann
247*1a17032bSKristof UmannSo we're talking about one more operation over the program state (scanning
248*1a17032bSKristof Umannreachable symbols and regions) that cannot work without checker support.
249*1a17032bSKristof Umann
250*1a17032bSKristof UmannWe can probably add a new callback "checkReachableSymbols" to solve this. This
251*1a17032bSKristof Umannis in fact also related to the dead symbols problem (we're scanning for live
252*1a17032bSKristof Umannsymbols in the store and in the checkers separately, but we need to do so
253*1a17032bSKristof Umannsimultaneously with a single worklist). Hmm, in fact this sounds like a good
254*1a17032bSKristof Umannidea; we can replace checkLiveSymbols with checkReachableSymbols.
255*1a17032bSKristof Umann
256*1a17032bSKristof UmannOr we could just have ghost member variables, and no checker support required at
257*1a17032bSKristof Umannall. For ghost member variables, the relation with their parent region (which
258*1a17032bSKristof Umannwould be their superregion) is actually useful, the mutability of their contents
259*1a17032bSKristof Umannis expressed naturally, and the store automagically sees reachable symbols, live
260*1a17032bSKristof Umannsymbols, escapes, invalidations, whatever.
261*1a17032bSKristof Umann
262*1a17032bSKristof Umann> In my view this differs from ghost variables in that (1) this storage does
263*1a17032bSKristof Umann> actually exist (it is just a library implementation detail where that storage
264*1a17032bSKristof Umann> lives) and (2) it is perfectly valid for a pointer into that storage to be
265*1a17032bSKristof Umann> returned and for another part of the program to read or write from that
266*1a17032bSKristof Umann> storage. (Well, in this case just read since it is allowed to be read-only
267*1a17032bSKristof Umann> memory).
268*1a17032bSKristof Umann
269*1a17032bSKristof Umann> What I'm not OK with is modeling abstract analysis state (for example, the
270*1a17032bSKristof Umann> count of a NSMutableArray or the typestate of a file handle) as a value stored
271*1a17032bSKristof Umann> in some ginned up region in the store.This takes an easy problem that the
272*1a17032bSKristof Umann> analyzer does well at (modeling typestate) and turns it into a hard one that
273*1a17032bSKristof Umann> the analyzer is bad at (reasoning about the contents of the heap).
274*1a17032bSKristof Umann
275*1a17032bSKristof UmannYeah, i tend to agree on that. For simple typestates, this is probably an
276*1a17032bSKristof Umannoverkill, so let's definitely put aside the idea of "ghost symbolic regions"
277*1a17032bSKristof Umannthat i had earlier.
278*1a17032bSKristof Umann
279*1a17032bSKristof UmannBut, to summarize a bit, in our current case, however, the typestate we're
280*1a17032bSKristof Umannlooking for is the contents of the heap. And when we try to model such
281*1a17032bSKristof Umanntypestates (complex in this specific manner, i.e. heap-like) in any checker, we
282*1a17032bSKristof Umannhave a choice between re-doing this modeling in every such checker (which is
283*1a17032bSKristof Umannsomething analyzer is indeed good at, but at a price of making checkers heavy)
284*1a17032bSKristof Umannor instead relying on the Store to do exactly what it's designed to do.
285*1a17032bSKristof Umann
286*1a17032bSKristof Umann> I think the key criterion here is: "is the region accessible from outside
287*1a17032bSKristof Umann> the library". That is, does the library expose the region as a pointer that
288*1a17032bSKristof Umann> can be read to or written from in the client program? If so, then it makes
289*1a17032bSKristof Umann> sense for this to be in the store: we are modeling reachable storage as
290*1a17032bSKristof Umann> storage. But if we're just modeling arbitrary analysis facts that need to be
291*1a17032bSKristof Umann> invalidated when a pointer escapes then we shouldn't try to gin up storage
292*1a17032bSKristof Umann> for them just to get invalidation for free.
293*1a17032bSKristof Umann
294*1a17032bSKristof UmannAs a metaphor, i'd probably compare it to body farms - the difference between
295*1a17032bSKristof Umannghost member variables and metadata symbols seems to me like the difference
296*1a17032bSKristof Umannbetween body farms and evalCall. Both are nice to have, and body farms are very
297*1a17032bSKristof Umannpleasant to work with, even if not omnipotent. I think it's fine for a
298*1a17032bSKristof UmannFunctionDecl's body in a body farm to have a local variable, even if such
299*1a17032bSKristof Umannvariable doesn't actually exist, even if it cannot be seen from outside the
300*1a17032bSKristof Umannfunction call. I'm not seeing immediate practical difference between "it does
301*1a17032bSKristof Umannactually exist" and "it doesn't actually exist, just a handy abstraction".
302*1a17032bSKristof UmannSimilarly, i think it's fine if we have a ``CXXRecordDecl`` with
303*1a17032bSKristof Umannimplementation-defined contents, and try to farm up a member variable as a handy
304*1a17032bSKristof Umannabstraction (we don't even need to know its name or offset, only that it's there
305*1a17032bSKristof Umannsomewhere).
306*1a17032bSKristof Umann
307*1a17032bSKristof Umann**Artem:**
308*1a17032bSKristof Umann
309*1a17032bSKristof UmannWe've discussed it in person with Devin, and he provided more points to think
310*1a17032bSKristof Umannabout:
311*1a17032bSKristof Umann
312*1a17032bSKristof Umann* If the initializer list consists of non-POD data, constructors of list's
313*1a17032bSKristof Umann  objects need to take the sub-region of the list's region as this-region In the
314*1a17032bSKristof Umann  current (v2) version of this patch, these objects are constructed elsewhere and
315*1a17032bSKristof Umann  then trivial-copied into the list's metadata pointer region, which may be
316*1a17032bSKristof Umann  incorrect. This is our overall problem with C++ constructors, which manifests in
317*1a17032bSKristof Umann  this case as well. Additionally, objects would need to be constructed in the
318*1a17032bSKristof Umann  analyzer's core, which would not be able to predict that it needs to take a
319*1a17032bSKristof Umann  checker-specific region as this-region, which makes it harder, though it might
320*1a17032bSKristof Umann  be mitigated by sharing the checker state traits.
321*1a17032bSKristof Umann
322*1a17032bSKristof Umann* Because "ghost variables" are not material to the user, we need to somehow
323*1a17032bSKristof Umann  make super sure that they don't make it into the diagnostic messages.
324*1a17032bSKristof Umann
325*1a17032bSKristof UmannSo, because this needs further digging into overall C++ support and rises too
326*1a17032bSKristof Umannmany questions, i'm delaying a better approach to this problem and will fall
327*1a17032bSKristof Umannback to the original trivial patch.
328