Sorry for the delay; I've been thinking about this problem for a while and I 
think I now have a reasonable design for it. Consider a case like this:

  struct Q { int a, b, c; };
  Q *f();
  void g() {
    struct A { Q q; } a = { *f(), .q.b = 3 };
  }

Here, we can't model the initialization of `a.q` as a partial merge (because we 
have no way to represent initializing `a.q.a` and `a.q.c` from the //same 
call// to `*f()`, but initializing `a.q.b` from `3`). I don't like the design 
of extending `InitListExpr` with a `PrevInitExpr` because it makes all users of 
that type need to cope with a weird special case; I'd prefer a more uniform 
representation.

So here's what I propose: add a `DesignatedInitUpdateExpr` that represents 
updating an initializer with another initializer by way of a designated 
initializer. This would have three members: the "base" initializer, the 
"update" initializer representing an initializer for a subobject of the "base", 
and a list of designators referring to the field of the base that is updated.

The evaluation semantics for a DIUE would be to first initialize the object 
using the "base" initializer, and then re-initialize the designated subobject 
using the "update" initializer. If the updated subobject is of a type that 
needs destruction, we'll need to pick between either running the destructor for 
the updated subobject, or rejecting designated initializations that would 
re-initialize a subobject with a non-trivial destructor (we may also need to 
take care if the updated subobject is a union member, because the initialized 
subobject might be a different one).

So in the above case, the initializer for `a` would be:

- An `InitListExpr` for `a`, with type `A`
- The init for field 0 (`q`) is a `DesignatedInitUpdateExpr`, with type `Q` and 
designator `b`
- The "base" for the DIUE is the expression `*f()`
- The "update" for the DIUE is the expression `3`

We could still use something like your `partialMergeFrom` functionality where 
possible, and only create `DesignatedInitUpdateExpr`s when necessary, but there 
are implications here: if an initializer has side-effects DIUE can preserve 
those side-effects even if the initializer is completely overridden, whereas 
partialMergeFrom will always lose the side-effects. The C standard allows 
either choice here, but I think we should be as consistent as we reasonably 
can. I suggest that we use `partialMergeFrom` wherever possible, and put the 
DIUE at the 'lowest' place possible within the initializer, so that we (attempt 
to) discard side-effects from all expressions that don't contribute to the 
final result. If we discard a side-effecting initializer in this way, we should 
issue a warning.

One other thing: `partialMergeFrom` should be a static function in 
`SemaInit.cpp`, not a member of `InitListExpr` (and maybe it might make more 
sense to integrate its functionality directly into `InitListChecker`). It's too 
high-level to belong on the AST.


http://reviews.llvm.org/D5789

EMAIL PREFERENCES
  http://reviews.llvm.org/settings/panel/emailpreferences/



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

Reply via email to