[PATCH] C99 partial re-initialization behavior (DR-253)
Richard Smith
richard at metafoo.co.uk
Mon Jan 12 13:46:33 PST 2015
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/
More information about the cfe-commits
mailing list