[clang] 538762f - Better diagnostics for anonymous bit-fields with attributes or an initializer.

Aaron Ballman via cfe-commits cfe-commits at lists.llvm.org
Tue Sep 29 13:32:39 PDT 2020


Author: Aaron Ballman
Date: 2020-09-29T16:32:20-04:00
New Revision: 538762fef0b662048be2a261ebc12da249efa977

URL: https://github.com/llvm/llvm-project/commit/538762fef0b662048be2a261ebc12da249efa977
DIFF: https://github.com/llvm/llvm-project/commit/538762fef0b662048be2a261ebc12da249efa977.diff

LOG: Better diagnostics for anonymous bit-fields with attributes or an initializer.

The current C++ grammar allows an anonymous bit-field with an attribute,
but this is ambiguous (the attribute in that case could appertain to the
type instead of the bit-field). The current thinking in the Core Working
Group is that it's better to disallow attributes in that position at the
grammar level so that the ambiguity resolves in favor of applying to the
type.

During discussions about the behavior of the attribute, the Core Working
Group also felt it was better to disallow anonymous bit-fields from
specifying a default member initializer.

This implements both sets of related grammar changes.

Added: 
    clang/test/CXX/class/class.bit/p1.cpp

Modified: 
    clang/include/clang/Basic/DiagnosticParseKinds.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/lib/Parse/ParseDecl.cpp
    clang/lib/Parse/ParseDeclCXX.cpp
    clang/lib/Sema/SemaDecl.cpp
    clang/test/Parser/MicrosoftExtensions.cpp
    clang/test/Parser/c2x-attributes.c

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index da4e1725269f..78d3a08b3028 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -872,6 +872,8 @@ def ext_bitfield_member_init: ExtWarn<
 def warn_cxx17_compat_bitfield_member_init: Warning<
   "default member initializer for bit-field is incompatible with "
   "C++ standards before C++20">, InGroup<CXXPre20Compat>, DefaultIgnore;
+def err_anon_bitfield_member_init : Error<
+  "anonymous bit-field cannot have a default member initializer">;
 def err_incomplete_array_member_init: Error<
   "array bound cannot be deduced from a default member initializer">;
 

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index ed11e0d1ce3c..4562d1114694 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5619,8 +5619,6 @@ def err_bitfield_width_exceeds_type_width : Error<
 def err_anon_bitfield_width_exceeds_type_width : Error<
   "width of anonymous bit-field (%0 bits) exceeds %select{width|size}1 "
   "of its type (%2 bit%s2)">;
-def err_anon_bitfield_init : Error<
-  "anonymous bit-field cannot have a default member initializer">;
 def err_incorrect_number_of_vector_initializers : Error<
   "number of elements must be either one or match the size of the vector">;
 

diff  --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index adec7c607682..3f314c59ade6 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4113,8 +4113,13 @@ void Parser::ParseStructDeclaration(
     DeclaratorInfo.D.setCommaLoc(CommaLoc);
 
     // Attributes are only allowed here on successive declarators.
-    if (!FirstDeclarator)
+    if (!FirstDeclarator) {
+      // However, this does not apply for [[]] attributes (which could show up
+      // before or after the __attribute__ attributes).
+      DiagnoseAndSkipCXX11Attributes();
       MaybeParseGNUAttributes(DeclaratorInfo.D);
+      DiagnoseAndSkipCXX11Attributes();
+    }
 
     /// struct-declarator: declarator
     /// struct-declarator: declarator[opt] ':' constant-expression

diff  --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 059d875d683d..a903896f172c 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -2305,10 +2305,15 @@ bool Parser::ParseCXXMemberDeclaratorBeforeInitializer(
     Declarator &DeclaratorInfo, VirtSpecifiers &VS, ExprResult &BitfieldSize,
     LateParsedAttrList &LateParsedAttrs) {
   // member-declarator:
-  //   declarator pure-specifier[opt]
+  //   declarator virt-specifier-seq[opt] pure-specifier[opt]
   //   declarator requires-clause
   //   declarator brace-or-equal-initializer[opt]
-  //   identifier[opt] ':' constant-expression
+  //   identifier attribute-specifier-seq[opt] ':' constant-expression
+  //       brace-or-equal-initializer[opt]
+  //   ':' constant-expression
+  //
+  // NOTE: the latter two productions are a proposed bugfix rather than the
+  // current grammar rules as of C++20.
   if (Tok.isNot(tok::colon))
     ParseDeclarator(DeclaratorInfo);
   else
@@ -2342,7 +2347,11 @@ bool Parser::ParseCXXMemberDeclaratorBeforeInitializer(
   }
 
   // If attributes exist after the declarator, but before an '{', parse them.
+  // However, this does not apply for [[]] attributes (which could show up
+  // before or after the __attribute__ attributes).
+  DiagnoseAndSkipCXX11Attributes();
   MaybeParseGNUAttributes(DeclaratorInfo, &LateParsedAttrs);
+  DiagnoseAndSkipCXX11Attributes();
 
   // For compatibility with code written to older Clang, also accept a
   // virt-specifier *after* the GNU attributes.
@@ -2784,7 +2793,12 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
     InClassInitStyle HasInClassInit = ICIS_NoInit;
     bool HasStaticInitializer = false;
     if (Tok.isOneOf(tok::equal, tok::l_brace) && PureSpecLoc.isInvalid()) {
-      if (DeclaratorInfo.isDeclarationOfFunction()) {
+      // DRXXXX: Anonymous bit-fields cannot have a brace-or-equal-initializer.
+      if (BitfieldSize.isUsable() && !DeclaratorInfo.hasName()) {
+        // Diagnose the error and pretend there is no in-class initializer.
+        Diag(Tok, diag::err_anon_bitfield_member_init);
+        SkipUntil(tok::comma, StopAtSemi | StopBeforeMatch);
+      } else if (DeclaratorInfo.isDeclarationOfFunction()) {
         // It's a pure-specifier.
         if (!TryConsumePureSpecifier(/*AllowFunctionDefinition*/ false))
           // Parse it as an expression so that Sema can diagnose it.
@@ -2933,7 +2947,11 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
     DeclaratorInfo.setCommaLoc(CommaLoc);
 
     // GNU attributes are allowed before the second and subsequent declarator.
+    // However, this does not apply for [[]] attributes (which could show up
+    // before or after the __attribute__ attributes).
+    DiagnoseAndSkipCXX11Attributes();
     MaybeParseGNUAttributes(DeclaratorInfo);
+    DiagnoseAndSkipCXX11Attributes();
 
     if (ParseCXXMemberDeclaratorBeforeInitializer(
             DeclaratorInfo, VS, BitfieldSize, LateParsedAttrs))

diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index aff49b7ddb90..1c3c48419680 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -16708,14 +16708,6 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T,
       BitWidth = nullptr;
       ZeroWidth = false;
     }
-
-    // Only data members can have in-class initializers.
-    if (BitWidth && !II && InitStyle) {
-      Diag(Loc, diag::err_anon_bitfield_init);
-      InvalidDecl = true;
-      BitWidth = nullptr;
-      ZeroWidth = false;
-    }
   }
 
   // Check that 'mutable' is consistent with the type of the declaration.

diff  --git a/clang/test/CXX/class/class.bit/p1.cpp b/clang/test/CXX/class/class.bit/p1.cpp
new file mode 100644
index 000000000000..ab15e3a356cf
--- /dev/null
+++ b/clang/test/CXX/class/class.bit/p1.cpp
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
+
+// Test various bit-field member declarations.
+constexpr int foo() { return 1; }
+struct A {
+  int a [[]] : 1;
+  int b, [[]] : 0; // expected-error {{an attribute list cannot appear here}}
+  int [[]] : 0; // OK, attribute applies to the type.
+  int [[]] c : 1; // OK, attribute applies to the type.
+  int : 2 = 1; // expected-error {{anonymous bit-field cannot have a default member initializer}}
+  int : 0 { 1 }; // expected-error {{anonymous bit-field cannot have a default member initializer}}
+  int : 0, d : 1 = 1;
+  int : 1 = 12, e : 1; // expected-error {{anonymous bit-field cannot have a default member initializer}}
+  int : 0, f : 1 = 1;
+  int g [[]] : 1 = 1;
+  int h [[]] : 1 {1};
+  int i : foo() = foo();
+  int j, [[]] k; // expected-error {{an attribute list cannot appear here}}
+};

diff  --git a/clang/test/Parser/MicrosoftExtensions.cpp b/clang/test/Parser/MicrosoftExtensions.cpp
index 52f40677a135..9706eecce2db 100644
--- a/clang/test/Parser/MicrosoftExtensions.cpp
+++ b/clang/test/Parser/MicrosoftExtensions.cpp
@@ -466,6 +466,6 @@ namespace enum_class {
     // MSVC produces a "C4353 constant 0 as function expression" for this,
     // considering the final {} to be part of the bit-width. We follow P0683R1
     // and treat it as a default member initializer.
-    enum E : int : int{}{}; // expected-error {{anonymous bit-field cannot have a default member initializer}} expected-warning {{C++20 extension}}
+    enum E : int : int{}{}; // expected-error {{anonymous bit-field cannot have a default member initializer}}
   };
 }

diff  --git a/clang/test/Parser/c2x-attributes.c b/clang/test/Parser/c2x-attributes.c
index 97f17ad4e7c1..393506e867fe 100644
--- a/clang/test/Parser/c2x-attributes.c
+++ b/clang/test/Parser/c2x-attributes.c
@@ -23,6 +23,9 @@ struct [[]] S1 {
   int l[[]][10];
   [[]] int m, n;
   int o [[]] : 12;
+  int [[]] : 0; // OK, attribute applies to the type.
+  int p, [[]] : 0; // expected-error {{an attribute list cannot appear here}}
+  int q, [[]] r; // expected-error {{an attribute list cannot appear here}}
 };
 
 [[]] struct S2 { int a; }; // expected-error {{misplaced attributes}}


        


More information about the cfe-commits mailing list