================
Comment at: lib/Analysis/UninitializedValues.cpp:571
@@ +570,3 @@
+  if (ME == findLeftmostMemberExpr(ME) &&
+      ME->getMemberDecl()->getType()->isReferenceType()) {
+    classify(ME, Use);
----------------
Enrico Pertoso wrote:
> Richard Smith wrote:
> > Seems this case should apply if any of the `FieldDecls` in the chain are of 
> > reference type, not just if the last one is.
> I'm not sure I understand:
> 
>   lang=c++
>   struct A {
>     int &a;
>   };
>   struct B {
>     A &a;
>     B(int) {}
>   };
>   struct C {
>     B &ref;
>     B b;
>     C() : b(ref.a.a) {}
>     C(B &o) : b(o.a.a) {}
>   };
>   
> The only thing we know here is that "ref" is uninitialized.
> Do you have anything specific in mind?
Suppose we had:

  struct A { int n; };
  struct B { A &a; };
  struct C {
    int n;
    B b;
    C(A &a) : b{a}, n(b.a.n = 0) {}
  };

Here (because the `n` initializer runs before the `b` initializer), `b` is 
uninitialized in the `b.a.n = 0` expression. Because `b.a` is a reference, that 
expression is a use of `b`, not an initialization of it. So we should diagnose 
this.

================
Comment at: lib/Analysis/UninitializedValues.cpp:581-586
@@ -426,1 +580,8 @@
 
+void ClassifyRefs::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE) {
+  if (DIE->getExpr() == 0)
+    return;
+  CXXDefaultInitExprVisitor<ClassifyRefs> V(*this);
+  V.Visit((Stmt*) DIE->getExpr());
+}
+
----------------
Enrico Pertoso wrote:
> Richard Smith wrote:
> > This looks suspicious. If we're visiting a `CXXDefaultInitExpr` for an 
> > object other than `this`, and that initializer references `this`, we'll 
> > think that we have a reference to one of our subobjects, but we don't.
> ##isTrackedField## verifies that the specified field has the same parent of 
> the constructor. A ##CXXDefaultInitExpr## appears either in a 
> ##CtorInitializer## or within the context of (c++1y) aggregate 
> initialization, in which case the record of the current constructor and that 
> of any ##MemberExpr## in the aggregate can't be the same.
I don't think that is the case. For instance:

  struct A {
    int a;
    int b;
    int c;
    struct B;
    A();
  };
  struct A::B : A {
    int d = (c = 0);
  };
  A::A() : a(B{}.d), b(c) {} // B{} should not mark our `c` as initialized.

As I alluded to in another comment, I think we could handle this correctly by 
not walking into a `CXXDefaultInitExpr` in aggregate initialization (perhaps 
only walk into them when they appear at the top level in a `CtorInitializer`, 
and do that in the CFG builder).

================
Comment at: lib/Analysis/UninitializedValues.cpp:795-796
@@ +794,4 @@
+      E = UO->getSubExpr()->IgnoreParenCasts();
+    else if (const ArraySubscriptExpr *ASE = dyn_cast<ArraySubscriptExpr>(E))
+      E = ASE->getLHS()->IgnoreParenCasts();
+  } while (OE != E);
----------------
Enrico Pertoso wrote:
> Richard Smith wrote:
> > I assume you're trying to cope with `this[0].blah` here? This doesn't look 
> > quite right: it'll also find `this[-1].blah` (which might sometimes be 
> > valid) and will do the wrong thing on `0[this].blah`. Maybe just drop the 
> > array indexing support here -- it's a corner case and not worth the cost of 
> > getting all the details right.
> Actually no. This just detects things like
> 
>   lang=c++
>   struct X {
>     int x, y;
>     X(int f) {
>       reinterpret_cast<int*>(this)[0] = f;
>       y = x;
>     }
>   };
> 
> Warning on ##y = x## is wrong. On the other hand, I'm simply doing as if 
> ##this## escaped analysis here, and yes, it won't work on 
> ##0[reinterpret_cast<int*>(this)] = f##.
> It's still seems to be a corner case, though, plus it's not warning on member 
> access via ##this[0]##.
> 
> Should I still drop this?
Unless you have real-world examples of bugs the extra accuracy catches here, 
I'd prefer to drop this for simplicity's sake.

================
Comment at: lib/Analysis/UninitializedValues.cpp:317-323
@@ +316,9 @@
+  void VisitStmt(Stmt* S) {
+    llvm::SmallVector<Stmt*, 12> children;
+    for (Stmt::child_range I = S->children(); I; ++I) {
+      children.push_back(*I);
+    }
+    for (std::size_t i = children.size(); i > 0; --i) {
+      Visit(children[i - 1]);
+    }
+  }
----------------
Enrico Pertoso wrote:
> Richard Smith wrote:
> > Why do you need the extra complexity of building a list and iterating 
> > backwards here, rather than just visiting the children inside the first 
> > loop?
> That's the way statements are appended to the CFG in the base case.
> But it doesn't seem to cause problems if I don't do it here (##classify## is 
> always called when there's enough context).
> 
Hmm, does this do good things if there is control flow in the 
`CXXDefaultInitExpr`?

  struct S {
    enum Kind { Foo, Bar } K;
    int FooVal;
    int BarVal;
    int Value = K == Foo ? FooVal : BarVal;
    S() : K(Foo), FooVal(0) {} // warning about uninitialized use of BarVal?
  };

Maybe we should teach the CFG builder to inline `CXXDefaultInitExprs` when they 
appear as a `CXXCtorInitializer`? That should not break the CFG's invariants, 
and allow you to drop a lot (all?) of your special-case handling for them 
(since you don't actually want to walk into them when they occur within 
aggregate initialization).

================
Comment at: lib/Analysis/UninitializedValues.cpp:555
@@ +554,3 @@
+    case CK_ArrayToPointerDecay:
+      classify(CE->getSubExpr(), Ignore);
+      break;
----------------
Enrico Pertoso wrote:
> Richard Smith wrote:
> > This looks curious. Treating this case as `Init` seems appropriate to me: 
> > we don't track how the resulting pointer is used, and it might be used to 
> > initialize the object. Is the idea to ignore all array subobjects of class 
> > type in the analysis? That is:
> > 
> >   struct X {
> >     int a[3];
> >     int b;
> >   };
> >   struct Y {
> >     X x;
> >     Y() {
> >       x.a[0] = 1; // don't treat 'x' as initialized
> >       x.a[1] = x.b; // warning: 'x' used uninitialized
> >     }
> >   };
> > 
> > If so, this really deserves a comment.
> This is actually another case in which ##x## is treated as initialized 
> because the default constructor is called, hence ##x## is immediately treated 
> as initialized.
> The analysis could potentially be made smarter for aggregates, but I'm not 
> sure this patch is the best place to do it.
> 
> This was intended for:
> 
>   lang=c++
>   void foo(int *a);
>   struct X {
>     int a[3];
>     int b;
>     X() {
>       foo(a);
>       b = a[1]; // do not warn on 'a' here.
>     }
>   };
> 
> But actually it's not necessary because the current code is not classifying 
> ##ArraySubscriptExprs##.
> Should I try to deal with ##ArraySubscriptExpr## it in this same patch?
My preference would be to keep this patch as small as possible while adding the 
new constructor-handling functionality. I'd prefer for `ArraySubscriptExpr` 
support to be added separately if that's practical.

================
Comment at: test/SemaCXX/uninitialized.cpp:444
@@ +443,3 @@
+    J() : a(1) {
+      float f = b;
+      b = -f;
----------------
Enrico Pertoso wrote:
> Richard Smith wrote:
> > We should really warn on this. A FIXME for that would be nice.
> I don't think we should. ##J## is a union, and one of its fields (##a##) has 
> been initialized.
> If you're really sure that a warning should still be issued here, it's 
> actually easier to do so rather than not.
Well, it's undefined behavior to write to field `a` then read from field `b`. 
The "uninitialized" warning isn't really the right one to issue, though, so I 
think your approach in this patch is fine.


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

Reply via email to