[cfe-commits] [PATCH] Implement C++11 in-class member initializers

Richard Smith richard at metafoo.co.uk
Mon May 23 17:48:36 PDT 2011


Hi all,

The attached patch adds support for C++11's in-class initialization of
non-static data members:

  struct S {
    int n = 0;
  };

For the most part, this is straightforward: the initializer is stored in
the FieldDecl, parsing is delayed until the outermost class is complete
(as is effectively required by the standard), and CXXConstructorInit has
been extended to optionally pick the initializer from the FieldDecl (this
allows the constructor definition to be built before the initializer is
parsed).

There is a hotch-potch of horrifying holes here, however:

1) C++11 doesn't allow in-class initialization of bit-fields. This would
be a useful extension, and I think it's simply an oversight in the
standard. I'm currently packing the initializer and bitfield width into
the same pointer, so some rework is required to support this.

2) There's an ordering issue with the implicit exception specification for
a defaulted default constructor and in-class initializers. Consider:

  template<bool b> struct T { typedef int f; };
  template<> struct T<true> { static void f(); };
  struct S {
    bool b = T<noexcept(S())>::f();
  };

This is well-formed, but is meaningless: if S::S() is noexcept, then it is
not, and vice versa. With a little tweaking, we can make it impossible to
parse such things at all. I have implemented an ad-hoc rule to allow clang
to try to escape from this pit with some dignity: while performing the
deferred parsing of in-class initializers, the exception specification of
the constructor (if needed) is temporarily set to a new value,
EST_Unknown, and any expression which depends on whether it is noexcept is
considered ill-formed and diagnosed.

Eventually, the same rule will need to be used while performing deferred
parsing for default arguments and exception specifications, which suffer
from the same problem: see test/SemaCXX/implicit-exception-spec.cpp.
Implicit constexpr on defaulted default constructors will also suffer a
similar problem, as will exception specifications for inherited
constructors (if the standard is fixed to compute them correctly).

3) Similarly to issue (2), we have a problem with the
__has_nothrow_constructor type trait within the definition of a class with
an in-class initializer. The specification for this trait says that it
shall be true only if the constructor is known to be non-throwing, which
isn't the case until we have parsed all the in-class initializers.
Therefore it currently produces 'false' in such cases (and the value can
subsequently be 'true' once the class is fully parsed). I'm not
particularly happy with this, and would prefer for us to diagnose such
uses.

4) The implicit exception specifications of defaulted default constructors
for classes containing in-class initializers, as implemented in this
patch, do not conform to the standard. The standard says that an implicit
exception specification throws an exception E iff any function it directly
invokes does. This is wrong in two ways:

  void f();
  struct S { int n = (throw "eep", 0); };
  struct T { bool b = nothrow(f()); };

In the first example, S::S() should be implicitly declared as noexcept,
because it does not invoke any throwing functions (despite containing a
throw-expression!). In the second example, S::S() should be implicitly
declared as noexcept(false), since the standard does not require a
function to be potentially-evaluated for it to be invoked ("invoked" isn't
formally defined, so this point is debatable, but that itself is a
defect).

I think clang should aim to implement the intent of the standard here: the
exception specification should include an exception E iff the implicit
body could throw E. This is partially implemented: the default constructor
is set to nothrow(false) if the in-class initializer can throw. A full
solution would require extending the Expr::CanThrow mechanism to provide a
list of potentially-thrown exceptions, but that is beyond the scope of
this patch (especially considering that it does not match the standard).

Another corner case we currently get wrong here is that the implicit
default constructor can invoke functions in a default argument for a base
or member default constructor, and such functions' exception
specifications are not currently being considered. This patch does nothing
to address that. That's PR9918.

5) This patch incorporates an interpretation of the proposed resolution to
C++ core issue 325. Specifically, we reject this:

  struct S {
    int n = T<0, 0>::f();
    template<int, int> struct T { static int f(); };
  };

because the ',' is interpreted as the end of the initializer.



A couple of other notes on the implementation:

 * There's an outstanding issue with initializers being provided for
typedefs in a class: currently clang asserts. This will be addressed by a
later patch.
 * There's a pre-existing issue with clang accepting initializers for
multiple members of a union: PR9996. This issue affects in-class
initializers too.
 * This patch also addresses in-class initialization of static members of
auto type.
 * We were parsing __attributes__/__asm__ and initializers in a
non-gcc-compatible order; I've swapped them over.

Apologies for the length of this email; I lack the time to make it shorter.

I'd welcome any and all comments!
Thanks, Richard
-------------- next part --------------
A non-text attachment was scrubbed...
Name: clang-in-class-member-init.diff
Type: text/x-patch
Size: 114183 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20110524/d0ba6e38/attachment.bin>


More information about the cfe-commits mailing list