hamzasood created this revision.

This patch implements P0683R1 <http://wg21.link/p0683r1>.

Member initialisers are allowed pre-C++11 as an extension. So I've also allowed 
bitfield member initialisers pre-C++2a as an extension (with appropriate 
warnings) for consistency.


https://reviews.llvm.org/D36611

Files:
  include/clang/AST/Decl.h
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticParseKinds.td
  lib/AST/Decl.cpp
  lib/Parse/ParseDeclCXX.cpp
  lib/Serialization/ASTReaderDecl.cpp
  lib/Serialization/ASTWriterDecl.cpp
  test/Parser/cxx2a-bitfield-member-init.cpp
  test/SemaCXX/member-init.cpp
  www/cxx_status.html

Index: www/cxx_status.html
===================================================================
--- www/cxx_status.html
+++ www/cxx_status.html
@@ -777,13 +777,12 @@
 
 <h2 id="cxx20">C++2a implementation status</h2>
 
-<p>Clang does not yet support any of the proposed features of
-<!--<p>Clang has <b>experimental</b> support for some proposed features of-->
+<p>Clang has <b>experimental</b> support for some proposed features of
 the C++ standard following C++17, provisionally named C++2a.
 Note that support for these features may change or be removed without notice,
 as the draft C++2a standard evolves.
 
-<!--<p>You can use Clang in C++2a mode with the <code>-std=c++2a</code> option.</p>-->
+<p>You can use Clang in C++2a mode with the <code>-std=c++2a</code> option.</p>
 
 <details open>
 <summary>List of features and minimum Clang version with support</summary>
@@ -798,7 +797,7 @@
     <tr>
       <td>Default member initializers for bit-fields</td>
       <td><a href="http://wg21.link/p0683r1";>P0683R1</a></td>
-      <td class="none" align="center">No</td>
+      <td class="svn" align="center">SVN</td>
     </tr>
     <tr>
       <td><tt>const&amp;</tt>-qualified pointers to members</td>
Index: test/SemaCXX/member-init.cpp
===================================================================
--- test/SemaCXX/member-init.cpp
+++ test/SemaCXX/member-init.cpp
@@ -1,7 +1,7 @@
 // RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -verify -std=c++11 -Wall %s
 
 struct Bitfield {
-  int n : 3 = 7; // expected-error {{bit-field member cannot have an in-class initializer}}
+  int n : 3 = 2; // expected-warning {{C++2a extension}}
 };
 
 int a;
Index: test/Parser/cxx2a-bitfield-member-init.cpp
===================================================================
--- test/Parser/cxx2a-bitfield-member-init.cpp
+++ test/Parser/cxx2a-bitfield-member-init.cpp
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -std=c++2a -verify %s
+// expected-no-diagnostics
+
+// This is a slightly modified version of the example from C++2a [class.mem]p7.
+// It tests the resolution of parsing ambiguities.
+
+int a;
+struct S {
+  unsigned x1 : 8 = 42;
+  unsigned x2 : 8 {42};
+
+  unsigned y1 : true ? 8 : a = 42;
+  unsigned y2 : (true ? 8 : a) = 42;
+
+  unsigned z1 : 1 || new int { 1 };
+  unsigned z2 : (1 || new int) { 1 };
+};
+
+constexpr S s{};
+static_assert(s.x1 == 42 && s.x2 == 42);
+static_assert(s.y1 == 0  && s.y2 == 42);
+static_assert(s.z1 == 0  && s.z2 == 1);
Index: lib/Serialization/ASTWriterDecl.cpp
===================================================================
--- lib/Serialization/ASTWriterDecl.cpp
+++ lib/Serialization/ASTWriterDecl.cpp
@@ -849,8 +849,8 @@
 void ASTDeclWriter::VisitFieldDecl(FieldDecl *D) {
   VisitDeclaratorDecl(D);
   Record.push_back(D->isMutable());
-  if (D->InitStorage.getInt() == FieldDecl::ISK_BitWidthOrNothing &&
-      D->InitStorage.getPointer() == nullptr) {
+  Record.AddStmt(D->getBitWidth());
+  if (D->InitStorage.getInt() == FieldDecl::ISK_Nothing) {
     Record.push_back(0);
   } else if (D->InitStorage.getInt() == FieldDecl::ISK_CapturedVLAType) {
     Record.push_back(D->InitStorage.getInt() + 1);
Index: lib/Serialization/ASTReaderDecl.cpp
===================================================================
--- lib/Serialization/ASTReaderDecl.cpp
+++ lib/Serialization/ASTReaderDecl.cpp
@@ -1219,9 +1219,10 @@
 void ASTDeclReader::VisitFieldDecl(FieldDecl *FD) {
   VisitDeclaratorDecl(FD);
   FD->Mutable = Record.readInt();
-  if (int BitWidthOrInitializer = Record.readInt()) {
+  FD->BitWidth = Record.readExpr();
+  if (int Initializer = Record.readInt()) {
     FD->InitStorage.setInt(
-          static_cast<FieldDecl::InitStorageKind>(BitWidthOrInitializer - 1));
+          static_cast<FieldDecl::InitStorageKind>(Initializer - 1));
     if (FD->InitStorage.getInt() == FieldDecl::ISK_CapturedVLAType) {
       // Read captured variable length array.
       FD->InitStorage.setPointer(Record.readType().getAsOpaquePtr());
Index: lib/Parse/ParseDeclCXX.cpp
===================================================================
--- lib/Parse/ParseDeclCXX.cpp
+++ lib/Parse/ParseDeclCXX.cpp
@@ -2403,6 +2403,7 @@
 ///         declarator constant-initializer[opt]
 /// [C++11] declarator brace-or-equal-initializer[opt]
 ///         identifier[opt] ':' constant-expression
+///             brace-or-equal-initializer[opt][C++2a]
 ///
 ///       virt-specifier-seq:
 ///         virt-specifier
@@ -2720,10 +2721,12 @@
     InClassInitStyle HasInClassInit = ICIS_NoInit;
     bool HasStaticInitializer = false;
     if (Tok.isOneOf(tok::equal, tok::l_brace) && PureSpecLoc.isInvalid()) {
-      if (BitfieldSize.get()) {
-        Diag(Tok, diag::err_bitfield_member_init);
-        SkipUntil(tok::comma, StopAtSemi | StopBeforeMatch);
-      } else if (DeclaratorInfo.isDeclarationOfFunction()) {
+      if (BitfieldSize.get())
+        Diag(Tok, !getLangOpts().CPlusPlus2a
+                      ? diag::ext_bitfield_member_init_cxx2a
+                      : diag::warn_cxx1z_compat_bitfield_member_init);
+
+      if (DeclaratorInfo.isDeclarationOfFunction()) {
         // It's a pure-specifier.
         if (!TryConsumePureSpecifier(/*AllowFunctionDefinition*/ false))
           // Parse it as an expression so that Sema can diagnose it.
Index: lib/AST/Decl.cpp
===================================================================
--- lib/AST/Decl.cpp
+++ lib/AST/Decl.cpp
@@ -3553,7 +3553,6 @@
 
 unsigned FieldDecl::getBitWidthValue(const ASTContext &Ctx) const {
   assert(isBitField() && "not a bitfield");
-  auto *BitWidth = static_cast<Expr *>(InitStorage.getPointer());
   return BitWidth->EvaluateKnownConstInt(Ctx).getZExtValue();
 }
 
@@ -3578,14 +3577,14 @@
 
 SourceRange FieldDecl::getSourceRange() const {
   switch (InitStorage.getInt()) {
-  // All three of these cases store an optional Expr*.
-  case ISK_BitWidthOrNothing:
+  // Both of these cases store an optional Expr*.
   case ISK_InClassCopyInit:
   case ISK_InClassListInit:
     if (const auto *E = static_cast<const Expr *>(InitStorage.getPointer()))
       return SourceRange(getInnerLocStart(), E->getLocEnd());
     // FALLTHROUGH
 
+  case ISK_Nothing:
   case ISK_CapturedVLAType:
     return DeclaratorDecl::getSourceRange();
   }
@@ -3595,9 +3594,9 @@
 void FieldDecl::setCapturedVLAType(const VariableArrayType *VLAType) {
   assert((getParent()->isLambda() || getParent()->isCapturedRecord()) &&
          "capturing type in non-lambda or captured record.");
-  assert(InitStorage.getInt() == ISK_BitWidthOrNothing &&
+  assert(InitStorage.getInt() == ISK_Nothing &&
          InitStorage.getPointer() == nullptr &&
-         "bit width, initializer or captured type already set");
+         "initializer or captured type already set");
   InitStorage.setPointerAndInt(const_cast<VariableArrayType *>(VLAType),
                                ISK_CapturedVLAType);
 }
Index: include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- include/clang/Basic/DiagnosticParseKinds.td
+++ include/clang/Basic/DiagnosticParseKinds.td
@@ -730,11 +730,17 @@
 def warn_cxx98_compat_nonstatic_member_init : Warning<
   "in-class initialization of non-static data members is incompatible with C++98">,
   InGroup<CXX98Compat>, DefaultIgnore;
-def err_bitfield_member_init: Error<
-  "bit-field member cannot have an in-class initializer">;
 def err_incomplete_array_member_init: Error<
   "array bound cannot be deduced from an in-class initializer">;
 
+// C++2a in-class bitfield member initialization
+def warn_cxx1z_compat_bitfield_member_init : Warning<
+  "in-class initialization of bit-field members is incompatible with "
+  "C++ standards before C++2a">, InGroup<CXXPre2aCompat>, DefaultIgnore;
+def ext_bitfield_member_init_cxx2a : ExtWarn<
+  "in-class initialization of bit-field members is a C++2a extension">,
+  InGroup<CXX2a>;
+
 // C++11 alias-declaration
 def ext_alias_declaration : ExtWarn<
   "alias declarations are a C++11 extension">, InGroup<CXX11>;
Index: include/clang/Basic/DiagnosticGroups.td
===================================================================
--- include/clang/Basic/DiagnosticGroups.td
+++ include/clang/Basic/DiagnosticGroups.td
@@ -167,6 +167,9 @@
 def CXXPre1zCompat : DiagGroup<"c++98-c++11-c++14-compat">;
 def CXXPre1zCompatPedantic : DiagGroup<"c++98-c++11-c++14-compat-pedantic",
                                        [CXXPre1zCompat]>;
+def CXXPre2aCompat : DiagGroup<"c++98-c++11-c++14-c++1z-compat">;
+def CXXPre2aCompatPedantic : DiagGroup<"c++98-c++11-c++14-c++1z-compat-pedantic",
+                                       [CXXPre2aCompat]>;
 
 def CXX98CompatBindToTemporaryCopy :
   DiagGroup<"c++98-compat-bind-to-temporary-copy">;
@@ -780,6 +783,10 @@
 // earlier C++ versions.
 def CXX1z : DiagGroup<"c++1z-extensions">;
 
+// A warning group for warnings about using C++2a features as extensions in
+// earlier C++ versions.
+def CXX2a : DiagGroup<"c++2a-extensions">;
+
 def : DiagGroup<"c++0x-extensions", [CXX11]>;
 def : DiagGroup<"c++1y-extensions", [CXX14]>;
 
Index: include/clang/AST/Decl.h
===================================================================
--- include/clang/AST/Decl.h
+++ include/clang/AST/Decl.h
@@ -2369,15 +2369,17 @@
   unsigned Mutable : 1;
   mutable unsigned CachedFieldIndex : 31;
 
-  /// The kinds of value we can store in InitializerOrBitWidth.
+  /// \brief Expression representing the bit-width,
+  /// or nullptr if this field isn't a bitfield.
+  Expr *BitWidth;
+
+  /// The kinds of value we can store in InitStorage.
   ///
   /// Note that this is compatible with InClassInitStyle except for
   /// ISK_CapturedVLAType.
   enum InitStorageKind {
-    /// If the pointer is null, there's nothing special.  Otherwise,
-    /// this is a bitfield and the pointer is the Expr* storing the
-    /// bit-width.
-    ISK_BitWidthOrNothing = (unsigned) ICIS_NoInit,
+    /// Nothing special, the pointer should be null.
+    ISK_Nothing = (unsigned) ICIS_NoInit,
 
     /// The pointer is an (optional due to delayed parsing) Expr*
     /// holding the copy-initializer.
@@ -2392,12 +2394,11 @@
     ISK_CapturedVLAType,
   };
 
-  /// \brief Storage for either the bit-width, the in-class
-  /// initializer, or the captured variable length array bound.
+  /// \brief Storage for either the in-class initializer
+  /// or the captured variable length array bound.
   ///
   /// We can safely combine these because in-class initializers are
-  /// not permitted for bit-fields, and both are exclusive with VLA
-  /// captures.
+  /// exclusive with VLA captures.
   ///
   /// If the storage kind is ISK_InClassCopyInit or
   /// ISK_InClassListInit, but the initializer is null, then this
@@ -2410,10 +2411,8 @@
             QualType T, TypeSourceInfo *TInfo, Expr *BW, bool Mutable,
             InClassInitStyle InitStyle)
     : DeclaratorDecl(DK, DC, IdLoc, Id, T, TInfo, StartLoc),
-      Mutable(Mutable), CachedFieldIndex(0),
-      InitStorage(BW, (InitStorageKind) InitStyle) {
-    assert((!BW || InitStyle == ICIS_NoInit) && "got initializer for bitfield");
-  }
+      Mutable(Mutable), CachedFieldIndex(0), BitWidth(BW),
+      InitStorage(nullptr, (InitStorageKind) InitStyle) { }
 
 public:
   static FieldDecl *Create(const ASTContext &C, DeclContext *DC,
@@ -2433,8 +2432,7 @@
 
   /// \brief Determines whether this field is a bitfield.
   bool isBitField() const {
-    return InitStorage.getInt() == ISK_BitWidthOrNothing &&
-           InitStorage.getPointer() != nullptr;
+    return BitWidth != nullptr;
   }
 
   /// @brief Determines whether this is an unnamed bitfield.
@@ -2447,26 +2445,24 @@
   bool isAnonymousStructOrUnion() const;
 
   Expr *getBitWidth() const {
-    return isBitField()
-               ? static_cast<Expr *>(InitStorage.getPointer())
-               : nullptr;
+    return BitWidth;
   }
   unsigned getBitWidthValue(const ASTContext &Ctx) const;
 
   /// setBitWidth - Set the bit-field width for this member.
   // Note: used by some clients (i.e., do not remove it).
   void setBitWidth(Expr *Width) {
-    assert(InitStorage.getInt() == ISK_BitWidthOrNothing &&
-           InitStorage.getPointer() == nullptr &&
-           "bit width, initializer or captured type already set");
-    InitStorage.setPointerAndInt(Width, ISK_BitWidthOrNothing);
+    assert(InitStorage.getInt() != ISK_CapturedVLAType &&
+           BitWidth == nullptr &&
+           "bit width or captured type already set");
+    BitWidth = Width;
   }
 
   /// removeBitWidth - Remove the bit-field width from this member.
   // Note: used by some clients (i.e., do not remove it).
   void removeBitWidth() {
     assert(isBitField() && "no bitfield width to remove");
-    InitStorage.setPointerAndInt(nullptr, ISK_BitWidthOrNothing);
+    BitWidth = nullptr;
   }
 
   /// getInClassInitStyle - Get the kind of (C++11) in-class initializer which
@@ -2498,15 +2494,15 @@
   void setInClassInitializer(Expr *Init) {
     assert(hasInClassInitializer() &&
            InitStorage.getPointer() == nullptr &&
-           "bit width, initializer or captured type already set");
+           "initializer or captured type already set");
     InitStorage.setPointer(Init);
   }
 
   /// removeInClassInitializer - Remove the C++11 in-class initializer from this
   /// member.
   void removeInClassInitializer() {
     assert(hasInClassInitializer() && "no initializer to remove");
-    InitStorage.setPointerAndInt(nullptr, ISK_BitWidthOrNothing);
+    InitStorage.setPointerAndInt(nullptr, ISK_Nothing);
   }
 
   /// \brief Determine whether this member captures the variable length array
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to