Yet, it does work. And I don't think it's "by chance". The reason that adding 
the values helps, is because I am not *re*adding the values to the environment. 
The problem is that values were never put there in the first place. The current 
code for processBranch does not need them, because it uses some magic to find 
the correct atomic sub-expression (ResolveCondition()) that is used to decide 
the value of the condition at this point. This, of course, fails when we run 
through the conditions the second time, because it cannot find the right 
"clues" in the previous elements of the CFGBlock.

  That's why I thought the cleanest solution would be to start storing the 
values of the conditional expressions explicitly. It also turned out to be the 
easiest solution, since the liveness analysis already supports this quite well. 
The proof that this works is in the way the analysis works: it goes through the 
CFG backwards, and when it sees that a subexpression is needed as an input, the 
subexpression is marked as live. Then the expression stays live until we 
encounter it as a full expression in the CFG. Here, we mark it as dead (and 
it's sub-expressions again as live, etc.). In this setting it does not matter 
that the expression will be marked as "live" twice, it will just be live 
longer, which is exactly what we want. The only potential problem with this is 
that the liveness algorithm uses Stmt* as an identifier of the position in the 
CFG, which is no longer unique. A better choice would be something like the 
(CFGBlock *, position-in-the-block) pair, and I think that in!
  the long
  run it would be a good idea to switch to something like this. If you feel 
that is necessary, I am ready to do that. However, for now it should work like 
this, because even though the live set will be computed twice, it should be the 
same, as there are only destructor invocations between the two computations.

  That said, I don't think this patch is complete yet. The problem with it is 
that it actually doesn't add enough values to the environment. E.g., in the 
expression ((A && B) && C) || D, if B is false then I it's not sufficient to 
bind (A && B) to 0 (which I do already), I also need to mark ((A && B) && C) as 
false, because this is what the destructor test will be checking. I will start 
looking into how to do this now.

  PS: as a practical proof that the symbol reaper recognizes the conditional 
subexpressions as live, I have rigged the analyzer with some debugging output, 
and this is what it produced when run on the following program:
  $ cat ~/tmp/a.cc
    struct A {
      A();
      ~A();
      operator bool() const;
    };
    bool u();
    void f() {
      if ((u() || u()) && A()) {
      } else {
      }
    }
  $ bin/clang --analyze ~/tmp/a.cc
  ----- Position: 7.0
  Store (direct and default bindings), 0x0 :

  Ranges are empty.
  ----- Position: 7.2
  Store (direct and default bindings), 0x0 :


  Expressions:
   (0x6536e20,0x652b1a8) u : &code{u}
   (0x6536e20,0x652b1f8) u : &code{u}
  Ranges are empty.
  Expression 0x652b1f8 is live.
  Attempting to use saved value of 0x652b210...
  Saving condition value of 0x652b2f8 (1)
  ----- Position: 6.0
  Store (direct and default bindings), 0x6537d58 :
   (GlobalInternalSpaceRegion,0,default) : conj_$0{int}

   (GlobalSystemSpaceRegion,0,default) : conj_$1{int}



  Expressions:
   (0x6536e20,0x652b1f8) u : &code{u}
   (0x6536e20,0x652b210) u() : conj_$2{_Bool}
  Ranges of symbol values:
   conj_$2{_Bool} : { [0, 0] }
  ----- Position: 6.2
  Store (direct and default bindings), 0x6537d58 :
   (GlobalInternalSpaceRegion,0,default) : conj_$0{int}

   (GlobalSystemSpaceRegion,0,default) : conj_$1{int}



  Expressions:
   (0x6536e20,0x652b290) u : &code{u}
   (0x6536e20,0x652b2b8) u : &code{u}
  Ranges are empty.
  Expression 0x652b2b8 is live.
  Attempting to use saved value of 0x652b2f8... failed
  Saving condition value of 0x652b2f8 (1)
  Saving condition value of 0x652b2f8 (0)
  ----- Position: 4.0
  Store (direct and default bindings), 0x653eb50 :
   (GlobalInternalSpaceRegion,0,default) : conj_$3{int}

   (GlobalSystemSpaceRegion,0,default) : conj_$4{int}



  Expressions:
   (0x6536e20,0x652b2b8) u : &code{u}
   (0x6536e20,0x652b2d0) u() : conj_$5{_Bool}
   (0x6536e20,0x652b2f8) u() || u() : 0 S8b
  Ranges of symbol values:
   conj_$5{_Bool} : { [0, 0] }
  Expression 0x652b2f8 is live.
  Attempting to use saved value of 0x652b2f8...
  Saving condition value of 0x652b670 (0)
  Attempting to use saved value of 0x652b698...
  ----- Position: 0.0
  Store (direct and default bindings), 0x653eb50 :
   (GlobalInternalSpaceRegion,0,default) : conj_$3{int}

   (GlobalSystemSpaceRegion,0,default) : conj_$4{int}



  Expressions:
   (0x6536e20,0x652b2f8) u() || u() : 0 S8b
   (0x6536e20,0x652b670) (u() || u()) && struct A().operator _Bool() : 0 S8b
  Ranges are empty.
  ----- Position: 5.0
  Store (direct and default bindings), 0x653eb50 :
   (GlobalInternalSpaceRegion,0,default) : conj_$3{int}

   (GlobalSystemSpaceRegion,0,default) : conj_$4{int}



  Expressions:
   (0x6536e20,0x652b2b8) u : &code{u}
   (0x6536e20,0x652b2d0) u() : conj_$5{_Bool}
   (0x6536e20,0x652b2f8) u() || u() : 1 S8b
  Ranges of symbol values:
   conj_$5{_Bool} : { [1, 255] }
  Expression 0x652b2f8 is live.
  ----- Position: 5.4
  Store (direct and default bindings), 0x653fed0 :
   (GlobalInternalSpaceRegion,0,default) : conj_$7{int}

   (GlobalSystemSpaceRegion,0,default) : conj_$8{int}

   (temp_object{struct A,0x652b520},0,default) : conj_$6{int}



  Expressions:
   (0x6536e20,0x652b2f8) u() || u() : 1 S8b
   (0x6536e20,0x652b600) struct A().operator _Bool : &code{operator _Bool}
  Ranges are empty.
  Expression 0x652b2f8 is live.
  Expression 0x652b600 is live.
  ----- Position: 4.0
  Store (direct and default bindings), 0x653fd10 :
   (GlobalInternalSpaceRegion,0,default) : conj_$9{int}

   (GlobalSystemSpaceRegion,0,default) : conj_$10{int}



  Expressions:
   (0x6536e20,0x652b2f8) u() || u() : 1 S8b
   (0x6536e20,0x652b600) struct A().operator _Bool : &code{operator _Bool}
   (0x6536e20,0x652b630) struct A().operator _Bool() : conj_$11{_Bool}
   (0x6536e20,0x652b658) struct A().operator _Bool() : conj_$11{_Bool}
  Ranges are empty.
  Expression 0x652b2f8 is live.
  Expression 0x652b658 is live.
  Attempting to use saved value of 0x652b2f8...
  Attempting to use saved value of 0x652b698...

  Block 4 is the one where we decide whether we want to run the destructor of 
A. As you can see, the value we bound earlier to 0x652b2f8, has survived this 
far, and is still considered live, which is why we succeed in using it two 
lines below.

  Sorry for such a long essay, but I wanted to make it clear about what I think 
about this problem. (I also found that it helps me to clear some things up 
internally :) ).

http://llvm-reviews.chandlerc.com/D1259
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to