[PATCH] D40841: [analyzer] Fix a crash on C++17 AST for non-trivial construction into a trivial brace initializer.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Tue Dec 5 10:12:31 PST 2017


On 5 December 2017 at 09:08, Artem Dergachev via Phabricator via
cfe-commits <cfe-commits at lists.llvm.org> wrote:

> NoQ created this revision.
> Herald added subscribers: cfe-commits, rnkovacs.
>
> Yet another situation where we cannot easily guess, by looking at the CFG,
> the target region for C++ constructor call.
>
> Consider the following brace initialization:
>
>   struct A {
>     A();
>    };
>
>   struct B : public A {
>     int x;
>   };
>
> void foo() {
>
>   B b = {};
>
> }
>
> AST in C++14:
>
>   `-VarDecl 0x7ff07884eab8 <col:3, col:10> col:5 b 'struct B' cinit
>     `-CXXConstructExpr 0x7ff07887a940 <col:9, col:10> 'struct B' 'void
> (void) noexcept(false)' list zeroing
>
> AST in C++17:
>
>   `-VarDecl 0x7fb1cf012b18 <col:3, col:10> col:5 b 'struct B' cinit
>     `-InitListExpr 0x7fb1cf012f38 <col:9, col:10> 'struct B'
>       |-CXXConstructExpr 0x7fb1cf012fd0 <col:10> 'struct A' 'void (void)'
> list
>       `-ImplicitValueInitExpr 0x7fb1cf013000 <<invalid sloc>> 'int'
>
> CFG in C++14:
>
>   [B1]
>     1: {} (CXXConstructExpr, struct B)
>     2: B b = {};
>
> CFG in C++17:
>
>   [B1]
>     1: {} (CXXConstructExpr, struct A)
>     2: /*implicit*/(int)0
>     3: {}
>     4: B b = {};
>
> So, essentially, in C++17 we don't have the trivial constructor call for
> `B` present anywhere at all, neither in AST, nor in CFG.
>
> This causes a crash in the analyzer because he tries to find the target
> region for `CK_NonVirtualBase` constructor of `A` by looking at the parent
> stack frame, expecting to find the derived class constructor (for `B`)
> here, and then taking a base region within its this-object.
>
> This fix is a quick-and-dirty suppression for the crash. It follows the
> tradition of "when unsure, make up a temporary and construct into that,
> then discard the result" - which is wrong, but at least it gets some
> invalidation done.
>
> In our example it would be correct to construct into `base{A, b}`. There
> can also be situations when such initializer-list is instead
> lifetime-extended by a `const&` or `&&` reference (see the tests), so in
> this case we'd need to construct into a respective sub-region of the
> temporary (but not into the temporary itself, of course).
>
> Finally, from the perspective of checker development, i believe it would
> be useful to actually revive the constructor call, so that PreCall/PostCall
> event occured.


Note that there is no constructor call here. This is aggregate
initialization. And there's not really any part of this that's new, except
that a class with base classes is now an a aggregate. You'll see the same
kind of AST formed in all C++ language modes with a slightly modified
example:

```
struct A {
  A();
};

struct B {
  A a;
  int x;
};

void foo() {
  B b = {};
}
```

The analyzer should presumably treat the new example the exact same way it
treats that case.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20171205/db545d16/attachment.html>


More information about the cfe-commits mailing list