[prev in list] [next in list] [prev in thread] [next in thread] 

List:       cfe-commits
Subject:    [PATCH] D25475: [analyzer] Add a new SVal to support pointer-to-member operations.
From:       Devin Coughlin via Phabricator via cfe-commits <cfe-commits () lists ! llvm ! org>
Date:       2016-11-30 22:08:08
Message-ID: 8d6d2933153acf7028f10f982810f9f9 () localhost ! localdomain
[Download RAW message or body]

dcoughlin added a comment.

PointerToMemberData looks like it is on the right track! One thing that is still \
missing is using the base specifier list to figure out the correct subobject field to \
read and write from. This is important when there is non-virtual multiple \
inheritance, as there will be multiple copies of the same field in the same object.

Here are is an example where the current patch is not quite right; both of these \
should evaluate to true:

  void clang_analyzer_eval(int);
  
  struct B {
    int f;
  };
  struct L1 : public B { };
  struct R1 : public B { };
  struct M : public L1, R1 { };
  struct L2 : public M { };
  struct R2 : public M { };
  struct D2 : public L2, R2 { };
  
  
  void diamond() {
    M m;
  
    static_cast<L1 *>(&m)->f = 7;
    static_cast<R1 *>(&m)->f = 16;
  
    int L1::* pl1 = &B::f;
    int M::* pm_via_l1 = pl1;
  
    int R1::* pr1 = &B::f;
    int M::* pm_via_r1 = pr1;
  
    clang_analyzer_eval(m.*(pm_via_l1) == 7); // expected-warning {{TRUE}}
    clang_analyzer_eval(m.*(pm_via_r1) == 16); // expected-warning {{TRUE}}
  }

I suspect you'll need to do something similar to what \
StoreManager::evalDerivedToBase() does (or maybe just use that function) to figure \
out the correct location to read and write from when accessing via a pointer to \
member.

It would also be good to add some tests for the double diamond scenario to ensure the \
list of path specifiers is constructed in the right order. For example:

  void double_diamond() {
    D2 d2;
  
    static_cast<L1 *>(static_cast<L2 *>(&d2))->f = 1;
    static_cast<L1 *>(static_cast<R2 *>(&d2))->f = 2;
    static_cast<R1 *>(static_cast<L2 *>(&d2))->f = 3;
    static_cast<R1 *>(static_cast<R2 *>(&d2))->f = 4;
  
    clang_analyzer_eval(d2.*(static_cast<int D2::*>(static_cast<int \
L2::*>(static_cast<int L1::*>(&B::f)))) == 1); // expected-warning {{TRUE}}  \
clang_analyzer_eval(d2.*(static_cast<int D2::*>(static_cast<int \
R2::*>(static_cast<int L1::*>(&B::f)))) == 2); // expected-warning {{TRUE}}  \
clang_analyzer_eval(d2.*(static_cast<int D2::*>(static_cast<int \
L2::*>(static_cast<int R1::*>(&B::f)))) == 3); // expected-warning {{TRUE}}  \
clang_analyzer_eval(d2.*(static_cast<int D2::*>(static_cast<int \
R2::*>(static_cast<int R1::*>(&B::f)))) == 4); // expected-warning {{TRUE}}  }



================
Comment at: include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h:217
+
+  llvm::ImmutableList<const CXXBaseSpecifier *> consCXXBase(
+      const CXXBaseSpecifier *CBS,
----------------
NoQ wrote:
> Hmm, is it "construct"? Or "constrain"? I think a verbose name wouldn't hurt.
> In fact, i suspect that `consVals` are rather `conSVals`, where `con` stands for \
> "concatenate".
In functional paradigms, 'cons' is used to mean prepending an item the the beginning \
of a linked list. https://en.wikipedia.org/wiki/Cons

In my opinion, 'prepend' is better than 'cons', which I find super-confusing. I don't \
think 'concatenate' is quite right, since typically that operation combines two (or \
more) lists. 


================
Comment at: lib/StaticAnalyzer/Core/ExprEngineC.cpp:322
+          Bldr.generateNode(CastE, Pred, state);
+          continue;
+        }
----------------
These conditional fallthroughs make me very, very uncomfortable because they will \
broken if any of the intervening cases get special handling in the future.

I think it would safer to factor out code in the "destination" case (here \
'CK_LValueBitCast') into a function, call it directly, and then continue regardless \
of the branch.

Another possibility is to use gotos to directly jump to the default 'destination' \
case.


================
Comment at: lib/StaticAnalyzer/Core/ExprEngineC.cpp:472
+        }
+        // If getAs failed just fall through to default behaviour.
+      }
----------------
I think it would be good to be explicit about this fallthrough behavior, as well.


================
Comment at: lib/StaticAnalyzer/Core/ExprEngineC.cpp:899
+    case UO_AddrOf: {
+      // Process pointer-to-member address operation
+      const Expr *Ex = U->getSubExpr()->IgnoreParens();
----------------
Just sticking this in the middle of a fallthrough cascade seems quite brittle. For \
example, it relies on the sub expression of a unary deref never being a DeclRefExpr \
to a field. This may be guaranteed by Sema (?) but if so it is quite a non-obvious \
invariant to rely upon.

I think it would be better the restructure this so that the AddrOf case doesn't fall \
in the the middle of a fallthrough cascade. You could either factor out the default \
behavior into a function or use a goto.


================
Comment at: lib/StaticAnalyzer/Core/SValBuilder.cpp:298
 
+  case Stmt::UnaryOperatorClass: {
+    const UnaryOperator *UO = dyn_cast<UnaryOperator>(E);
----------------
Can this be removed? There are no tests for it.


https://reviews.llvm.org/D25475



_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic