This is an archive of the discontinued LLVM Phabricator instance.

[CFG] Fix automatic destructors when a member is bound to a reference.
ClosedPublic

Authored by NoQ on Mar 7 2018, 5:24 PM.

Details

Summary

In code like

const int &x = A().x;

the destructor for A() was not present in the CFG due to two problems in the pattern-matching in getReferenceInitTemporaryType():

  1. The no-op cast from T to const T is not necessarily performed on a record type. It may be performed on essentially any type. In the current example, it is performed on an xvalue of type int in order to turn it into a const int.
  2. Since rC288563 member access is no longer performed over rvalues, but goes after the MaterializeTemporaryExpr instead.

This is not an analyzer-specific change. It may add/remove compiler warnings, but i don't think it makes any sense to hide this behind an analyzer-specific flag.

Diff Detail

Repository
rC Clang

Event Timeline

NoQ created this revision.Mar 7 2018, 5:24 PM

Seems fine to me, but you might want someone with analyzer experience to review.

rsmith added inline comments.Mar 31 2018, 4:51 PM
lib/Analysis/CFG.cpp
1495–1496

Can you replace this with Expr::skipRValueSubobjectAdjustments? That's how we compute this elsewhere. (You'll need to skip a top-level ExprWithCleanups and check for the search terminating in a MaterializeTemporaryExpr yourself, but this will reduce duplication and fix a couple more cases this function is getting wrong).

NoQ added inline comments.Apr 4 2018, 11:39 AM
lib/Analysis/CFG.cpp
1495–1496

Hmm, right, ok, yeah. I'm in no rush with this one, so i'll spend some time trying to understand what rvalue adjustments do we still have after rC288563.

rsmith added inline comments.Apr 4 2018, 2:26 PM
lib/Analysis/CFG.cpp
1495–1496

We still have to deal with rvalue adjustments in C++98 compilations, because we don't create xvalue MaterializeTemporaryExpr nodes there (because C++98 doesn't have xvalues). I think if/when we carry out the plan to remove CXXBindTemporaryExpr, skipping rvalue adjustments will only be interesting to the code that performs lifetime extension, and everything else can just deal with the MaterializeTemporaryExpr nodes. But for now, you'll need to skip rvalue adjustments if you want to handle our C++98 ASTs.

As a quick example, take:

struct A { int n; };
const int &r = A().*&A::n;

... which produces this AST in C++11 onwards:

`-VarDecl 0xc2681f0 <col:22, col:46> col:33 r 'const int &' cinit
  `-ExprWithCleanups 0xc268b28 <col:37, col:46> 'const int' xvalue
    `-ImplicitCastExpr 0xc2689f0 <col:37, col:46> 'const int' xvalue <NoOp>
      `-BinaryOperator 0xc2689c8 <col:37, col:46> 'int' xvalue '.*'
        |-MaterializeTemporaryExpr 0xc2689b0 <col:37, col:39> 'A' xvalue extended by Var 0xc2681f0 'r' 'const int &'
        | `-CXXTemporaryObjectExpr 0xc2687b0 <col:37, col:39> 'A' 'void () noexcept' zeroing
        `-UnaryOperator 0xc268990 <col:42, col:46> 'int A::*' prefix '&' cannot overflow
          `-DeclRefExpr 0xc268928 <col:43, col:46> 'int' lvalue Field 0xc268148 'n' 'int'

... but produces this in C++98 mode (which I think this function will mishandle because it doesn't know how to look through the binary .* operator):

`-VarDecl 0xcd61c90 <col:22, col:46> col:33 r 'const int &' cinit
  `-ExprWithCleanups 0xcd62398 <col:37, col:46> 'const int' lvalue
    `-MaterializeTemporaryExpr 0xcd622a8 <col:37, col:46> 'const int' lvalue extended by Var 0xcd61c90 'r' 'const int &'
      `-BinaryOperator 0xcd62280 <col:37, col:46> 'int' '.*'
        |-CXXTemporaryObjectExpr 0xcd62080 <col:37, col:39> 'A' 'void () throw()' zeroing
        `-UnaryOperator 0xcd62260 <col:42, col:46> 'int A::*' prefix '&' cannot overflow
          `-DeclRefExpr 0xcd621f8 <col:43, col:46> 'int' lvalue Field 0xcd61be8 'n' 'int'
NoQ added inline comments.Apr 4 2018, 4:21 PM
lib/Analysis/CFG.cpp
1495–1496

Aha, yay, thanks, i'll add this one, and also it seems that CFG and Analyzer C++ tests need way more different -std= run-lines :)

NoQ updated this revision to Diff 147635.May 18 2018, 6:57 PM

Switch to skipRValueSubobjectAdjustments. Yup, that's much better.

Add FIXME tests for lifetime extension through non-references that are still not working - that correspond to a nearby FIXME in the code. I'm not planning to fix them immediately, but it's nice to know what else isn't working in this realm.

NoQ marked an inline comment as done.May 18 2018, 6:58 PM
george.karpenkov accepted this revision.May 30 2018, 4:52 PM

I'm not an expert in CFG construction, but looks good to me.

This revision is now accepted and ready to land.May 30 2018, 4:52 PM
This revision was automatically updated to reflect the committed changes.