emmettneyman updated this revision to Diff 208842.
emmettneyman added a comment.

slight change to specification reference format


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D64380/new/

https://reviews.llvm.org/D64380

Files:
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Sema/SemaDecl.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/test/Misc/pragma-attribute-supported-attributes-list.test
  clang/test/SemaCXX/attr-designated-init-required.cpp
  clang/test/SemaCXX/attr-require-designated-init.cpp

Index: clang/test/SemaCXX/attr-require-designated-init.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/attr-require-designated-init.cpp
@@ -0,0 +1,104 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+#define ATTR [[clang::require_designated_init]]
+
+// The require_designated_init attribute only applies to types. It will
+// generate a warning when attached to variables, functions, arrays, etc.
+int ATTR x;            // expected-error{{'require_designated_init' attribute cannot be applied to types}}
+void ATTR fun(int x) { // expected-error{{'require_designated_init' attribute cannot be applied to types}}
+  return;
+}
+int ATTR arr[10]; // expected-error{{'require_designated_init' attribute cannot be applied to types}}
+
+// Struct with one field with require_designated_init attribute
+struct ATTR Foo { // expected-note 0+ {{required by 'require_designated_init' attribute here}}
+  int a;
+};
+
+// The following are invalid ways of initializing instances of this struct.
+Foo f1;       // expected-error{{variable declaration does not use designated initializer syntax}}
+Foo f2{1};    // expected-error{{variable declaration does not use designated initializer syntax}}
+Foo f3 = {1}; // expected-error{{variable declaration does not use designated initializer syntax}}
+// The following are valid ways of initializing instances of this struct.
+Foo f4{};
+Foo f5 = {};
+Foo f6{.a = 1};
+Foo f7 = {.a = 1};
+
+// Struct with multiple fields wth require_designated_init attribute
+struct ATTR Bar { // expected-note 0+ {{required by 'require_designated_init' attribute here}}
+  int b;
+  int c;
+};
+
+// The following are invalid ways of initializing instances of this struct.
+Bar b1;               // expected-error{{variable declaration does not use designated initializer syntax}}
+Bar b2{1, 2};         // expected-error{{variable declaration does not use designated initializer syntax}}
+Bar b3 = {1, 2};      // expected-error{{variable declaration does not use designated initializer syntax}}
+Bar b4{.b = 1, 2};    // expected-error{{variable declaration does not use designated initializer syntax}}
+Bar b5 = {.b = 1, 2}; // expected-error{{variable declaration does not use designated initializer syntax}}
+// The following are valid ways of initializing instances of this struct.
+Bar b6{};
+Bar b7 = {};
+Bar b8{.b = 1};
+Bar b9 = {.b = 1};
+Bar b10{.b = 1, .c = 2};
+Bar b11 = {.b = 1, .c = 2};
+Bar b12 = {.c = 2, .b = 1};
+
+// Struct without require_designated_init attribute
+struct Baz {
+  int d;
+  int e;
+};
+
+// The following are all valid ways of initializing instances of this struct.
+Baz z1;
+Baz z2{};
+Baz z3 = {};
+Baz z4{1, 2};
+Baz z5 = {1, 2};
+Baz z6{.d = 1, .e = 2};
+Baz z7 = {.d = 1, .e = 2};
+Baz z8{1};
+Baz z9 = {1};
+Baz z10{.d = 1, 2};
+Baz z11 = {.d = 1, 2};
+
+// The require_designated_init attribute can also be attached to unions.
+union ATTR Uni { // expected-note 0+ {{required by 'require_designated_init' attribute here}}
+  int x;
+  int y;
+};
+
+// The following are invalid ways of initializing instances of this union.
+Uni u1;       // expected-error{{variable declaration does not use designated initializer syntax}}
+Uni u2{1};    // expected-error{{variable declaration does not use designated initializer syntax}}
+Uni u3 = {1}; // expected-error{{variable declaration does not use designated initializer syntax}}
+// The following are valid ways of initializing instances of this union.
+Uni u4{};
+Uni u5 = {};
+Uni u6{.x = 1};
+Uni u7 = {.x = 1};
+
+// The require_designated_init attribute can also be attached to classes.
+class ATTR Cla { // expected-note 0+ {{required by 'require_designated_init' attribute here}}
+public:
+  int x;
+  int y;
+};
+
+// The following are invalid ways of initializing instances of this class.
+Cla c1;               // expected-error{{variable declaration does not use designated initializer syntax}}
+Cla c2{1, 2};         // expected-error{{variable declaration does not use designated initializer syntax}}
+Cla c3 = {1, 2};      // expected-error{{variable declaration does not use designated initializer syntax}}
+Cla c4{.x = 1, 2};    // expected-error{{variable declaration does not use designated initializer syntax}}
+Cla c5 = {.x = 1, 2}; // expected-error{{variable declaration does not use designated initializer syntax}}
+// The following are valid ways of initializing instances of this class.
+Cla c6{};
+Cla c7 = {};
+Cla c8{.x = 1};
+Cla c9 = {.x = 1};
+Cla c10{.x = 1, .y = 2};
+Cla c11 = {.x = 1, .y = 2};
+Cla c12 = {.y = 2, .x = 1};
Index: clang/test/SemaCXX/attr-designated-init-required.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/attr-designated-init-required.cpp
@@ -0,0 +1,63 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+#define ATTR [[clang::designated_init_required]]
+
+ATTR int x;            // expected-warning{{'designated_init_required' attribute only applies to non-static data members}}
+ATTR void fun(int x) { // expected-warning{{'designated_init_required' attribute only applies to non-static data members}}
+  return;
+}
+struct ATTR Foo { // expected-warning{{'designated_init_required' attribute only applies to non-static data members}}
+  int x;
+};
+
+// Struct with one required field
+struct Bar {
+  ATTR int y; // expected-note 0+ {{enforced by 'designated_init_required' attribute here}}
+};
+
+// The following are invalid ways of initializing instances of this struct.
+Bar b1;    // expected-error{{initializer for variable b1 must explicitly initialize field y}}
+Bar b2{};  // expected-error{{initializer for variable b2 must explicitly initialize field y}}
+Bar b3{1}; // expected-error{{initializer for variable b3 must explicitly initialize field y}}
+
+// The following are valid ways of initializing instances of this struct.
+Bar b6{.y = 1};
+
+// Struct with multiple required fields
+struct Baz {
+  ATTR int x; // expected-note 0+ {{enforced by 'designated_init_required' attribute here}}
+  int y;
+  ATTR int z; // expected-note 0+ {{enforced by 'designated_init_required' attribute here}}
+};
+
+// The following are invalid ways of initializing instances of this struct.
+Baz z1;                 // expected-error{{initializer for variable z1 must explicitly initialize field x}} expected-error{{initializer for variable z1 must explicitly initialize field z}}
+Baz z2{};               // expected-error{{initializer for variable z2 must explicitly initialize field x}} expected-error{{initializer for variable z2 must explicitly initialize field z}}
+Baz z3{1, 2};           // expected-error{{initializer for variable z3 must explicitly initialize field x}} expected-error{{initializer for variable z3 must explicitly initialize field z}}
+Baz z4{1, 2, 3};        // expected-error{{initializer for variable z4 must explicitly initialize field x}} expected-error{{initializer for variable z4 must explicitly initialize field z}}
+Baz z5{.x = 1, 2};      // expected-error{{initializer for variable z5 must explicitly initialize field z}}
+Baz z6{.x = 1, .y = 2}; // expected-error{{initializer for variable z6 must explicitly initialize field z}}
+
+// The following are valid ways of initializing instances of this struct.
+Baz z7{.x = 1, .y = 2, .z = 3};
+Baz z8{.x = 1, .z = 3};
+Baz z9{.x = 1, 2, .z = 3};
+
+// The required attribute can also be applied to public fields of classes.
+class Cla {
+public:
+  ATTR int x; // expected-note 0+ {{enforced by 'designated_init_required' attribute here}}
+  int y;
+};
+
+// The following are invalid ways of initializing instances of this class.
+Cla c1;            // expected-error{{initializer for variable c1 must explicitly initialize field x}}
+Cla c2{};          // expected-error{{initializer for variable c2 must explicitly initialize field x}}
+Cla c3{1};         // expected-error{{initializer for variable c3 must explicitly initialize field x}}
+Cla c4{1, 2};      // expected-error{{initializer for variable c4 must explicitly initialize field x}}
+Cla c5{1, .y = 2}; // expected-error{{initializer for variable c5 must explicitly initialize field x}}
+
+// The following are valid ways of initializing instances of this class.
+Cla c6{.x = 1};
+Cla c7{.x = 1, .y = 2};
+Cla c8{.x = 1, 2};
Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -122,6 +122,7 @@
 // CHECK-NEXT: RenderScriptKernel (SubjectMatchRule_function)
 // CHECK-NEXT: ReqdWorkGroupSize (SubjectMatchRule_function)
 // CHECK-NEXT: RequireConstantInit (SubjectMatchRule_variable_is_global)
+// CHECK-NEXT: Required (SubjectMatchRule_field)
 // CHECK-NEXT: Restrict (SubjectMatchRule_function)
 // CHECK-NEXT: ReturnTypestate (SubjectMatchRule_function, SubjectMatchRule_variable_is_parameter)
 // CHECK-NEXT: ReturnsNonNull (SubjectMatchRule_objc_method, SubjectMatchRule_function)
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -5309,6 +5309,23 @@
                                          AL.getAttributeSpellingListIndex()));
 }
 
+static void handleRequireDesignatedInit(Sema &S, Decl *D,
+                                        const ParsedAttr &AL) {
+  if (TagDecl *TD = dyn_cast<TagDecl>(D))
+    TD->addAttr(::new (S.Context) RequireDesignatedInitAttr(
+        AL.getRange(), S.Context, AL.getAttributeSpellingListIndex()));
+  else
+    S.Diag(AL.getLoc(), diag::warn_attribute_ignored) << AL.getName();
+}
+
+static void handleRequired(Sema &S, Decl *D, const ParsedAttr &AL) {
+  if (FieldDecl *FD = dyn_cast<FieldDecl>(D))
+    FD->addAttr(::new (S.Context) RequiredAttr(
+        AL.getRange(), S.Context, AL.getAttributeSpellingListIndex()));
+  else
+    S.Diag(AL.getLoc(), diag::warn_attribute_ignored) << AL.getName();
+}
+
 static void handleObjCRuntimeName(Sema &S, Decl *D, const ParsedAttr &AL) {
   StringRef MetaDataName;
   if (!S.checkStringLiteralArgumentAttr(AL, 0, MetaDataName))
@@ -6959,6 +6976,12 @@
   case ParsedAttr::AT_RequireConstantInit:
     handleSimpleAttribute<RequireConstantInitAttr>(S, D, AL);
     break;
+  case ParsedAttr::AT_RequireDesignatedInit:
+    handleRequireDesignatedInit(S, D, AL);
+    break;
+  case ParsedAttr::AT_Required:
+    handleRequired(S, D, AL);
+    break;
   case ParsedAttr::AT_InitPriority:
     handleInitPriorityAttr(S, D, AL);
     break;
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -11211,6 +11211,82 @@
     Init = Result.get();
   }
 
+  if (auto *TD = VDecl->getType().getTypePtr()->getAsTagDecl()) {
+    // If the type of the declaration is a struct/class and that type has the
+    // require_designated_init attribute, check that the initializer has
+    // the proper designated initializer syntax.
+    if (TD->hasAttr<RequireDesignatedInitAttr>()) {
+      if (auto *ILE = dyn_cast<InitListExpr>(Init)) {
+        for (unsigned i = 0; i < ILE->getNumInits(); i++) {
+          Expr *init = ILE->getInit(i);
+          if (auto *DIE = dyn_cast<DesignatedInitExpr>(init))
+            continue;
+          SourceRange SR(VDecl->getSourceRange().getBegin(),
+                         Init->getSourceRange().getEnd());
+          Diag(init->getExprLoc(), diag::err_require_designated_init_failed)
+              << SR;
+          auto attr = TD->getAttr<RequireDesignatedInitAttr>();
+          Diag(attr->getLocation(),
+               diag::note_declared_required_designated_init_here)
+              << attr->getRange();
+          VDecl->setInvalidDecl();
+          return;
+        }
+      }
+    }
+
+    // If the type of the declaration is a struct/class, we must check whether
+    // any of the fields have the required attribute. If any of them do, we must
+    // confirm that each of those fields are initialized with designated
+    // initializer syntax.
+    if (RecordDecl *RD = dyn_cast<RecordDecl>(TD)) {
+      // Iterate through all the fields of the record and add all of the
+      // required fields to a set. The field will be removed later if it is
+      // properly initialized.
+      std::set<IdentifierInfo *> RequiredFields;
+      for (const auto *FD : RD->fields()) {
+        if (FD->hasAttr<RequiredAttr>()) {
+          RequiredFields.insert(FD->getIdentifier());
+        }
+      }
+      // Iterate through all the initializers and remove a field from the set if
+      // it is initialized correctly using designated initializer syntax.
+      if (RequiredFields.size() > 0) {
+        if (InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) {
+          for (unsigned I = 0, E = ILE->getNumInits(); I != E; I++) {
+            Expr *init = ILE->getInit(I);
+            if (DesignatedInitExpr *DIE = dyn_cast<DesignatedInitExpr>(init)) {
+              DesignatedInitExpr::Designator *D = DIE->getDesignator(0);
+              if (D->isFieldDesignator()) {
+                IdentifierInfo *name = D->getFieldName();
+                if (RequiredFields.count(name) != 0)
+                  RequiredFields.erase(name);
+              }
+            }
+          }
+        }
+        // Iterate through all the remaining fields and emit a diagnostic for
+        // each field.
+        for (auto FD : RD->fields()) {
+          if (RequiredFields.count(FD->getIdentifier()) != 0) {
+            SourceRange SR(VDecl->getSourceRange().getBegin(),
+                           Init->getSourceRange().getEnd());
+            Diag(Init->getExprLoc(), diag::err_required_failed)
+                << SR << VDecl->getName() << FD->getName();
+            auto attr = FD->getAttr<RequiredAttr>();
+            Diag(attr->getLocation(), diag::note_declared_required_here)
+                << attr->getRange();
+            VDecl->setInvalidDecl();
+            RequiredFields.erase(FD->getIdentifier());
+          }
+        }
+        // Return here so all attribute violation errors get emitted.
+        if (VDecl->isInvalidDecl())
+          return;
+      }
+    }
+  }
+
   // Perform the initialization.
   ParenListExpr *CXXDirectInit = dyn_cast<ParenListExpr>(Init);
   if (!VDecl->isInvalidDecl()) {
@@ -11552,6 +11628,39 @@
   if (VarDecl *Var = dyn_cast<VarDecl>(RealDecl)) {
     QualType Type = Var->getType();
 
+    if (TagDecl *TD = Type.getTypePtr()->getAsTagDecl()) {
+      // If the type of the declaration is a struct/class and that type has the
+      // require_designated_init attribute, an initializer is mandatory.
+      if (TD->hasAttr<RequireDesignatedInitAttr>()) {
+        Diag(Var->getLocation(), diag::err_require_designated_init_failed)
+            << Var->getSourceRange();
+        auto attr = TD->getAttr<RequireDesignatedInitAttr>();
+        Diag(attr->getLocation(),
+             diag::note_declared_required_designated_init_here)
+            << attr->getRange();
+        Var->setInvalidDecl();
+        return;
+      }
+      // If the type of the declaration is a struct/class, we must check whether
+      // any of the fields have the required attribute. For each that does, emit
+      // an error since it is not initialized with designated initializer
+      // syntax.
+      if (RecordDecl *RD = dyn_cast<RecordDecl>(TD)) {
+        for (auto FD : RD->fields()) {
+          if (FD->hasAttr<RequiredAttr>()) {
+            Diag(Var->getLocation(), diag::err_required_failed)
+                << Var->getSourceRange() << Var->getName() << FD->getName();
+            auto attr = FD->getAttr<RequiredAttr>();
+            Diag(attr->getLocation(), diag::note_declared_required_here)
+                << attr->getRange();
+            Var->setInvalidDecl();
+          }
+        }
+        if (Var->isInvalidDecl())
+          return;
+      }
+    }
+
     // C++1z [dcl.dcl]p1 grammar implies that an initializer is mandatory.
     if (isa<DecompositionDecl>(RealDecl)) {
       Diag(Var->getLocation(), diag::err_decomp_decl_requires_init) << Var;
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3530,6 +3530,16 @@
   "'objc_designated_initializer' attribute only applies to init methods "
   "of interface or class extension declarations">;
 
+def err_require_designated_init_failed : Error<
+  "variable declaration does not use designated initializer syntax">;
+def note_declared_required_designated_init_here : Note<
+  "required by 'require_designated_init' attribute here">;
+
+def err_required_failed : Error<
+  "initializer for variable %0 must explicitly initialize field %1">;
+def note_declared_required_here : Note<
+  "enforced by 'designated_init_required' attribute here">;
+
 // objc_bridge attribute diagnostics.
 def err_objc_attr_not_id : Error<
   "parameter of %0 attribute must be a single name of an Objective-C %select{class|protocol}1">;
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -1445,6 +1445,61 @@
   }];
 }
 
+def RequireDesignatedInitDocs : Documentation {
+  let Category = DocCatType;
+  let Content = [{
+This attribute can be applied to a struct definition to require that anytime a
+variable of that struct's type is declared, the declaration uses designated
+initializer syntax ([dcl.init.aggr]p3.1).
+For a struct ``Foo`` with one integer field ``x``, the following declarations
+are valid and invalid when this attribute is applied to the struct's definition.
+
+.. code-block:: c++
+
+  Foo foo {.x = 1}; // valid
+  Foo foo {}; // valid
+  Foo foo {1}; // invalid
+  Foo foo; // invalid
+
+For a struct ``Foo`` with three integer fields ``x``, ``y``, and ``z``, the
+following declarations are valid and invalid when this attribute is applied to
+the struct's definition.
+
+.. code-block:: c++
+
+  Foo foo {.x = 1, .y = 2, .z = 3}; // valid
+  Foo foo {.z = 3, .x = 1, .y = 2}; // valid
+  Foo foo {.x = 1, .z = 3}; // valid
+  Foo foo {}; // valid
+  Foo foo {1, 2, 3}; // invalid
+  Foo foo {.x = 1, 2, 3}; // invalid
+  Foo foo; // invalid
+  }];
+}
+
+def RequiredDocs : Documentation {
+  let Category = DocCatType;
+  let Content = [{
+This attribute can be applied to a field definition within a struct or a class
+to require that anytime a variable of the struct/class's type is declared, that
+field must be initialized using designated initializer syntax ([dcl.init.aggr]p3.1).
+A field marked with this attribute may not be omitted or default-constructed.
+For a struct ``Foo`` with a ``designated_init_required`` integer field ``x``,
+the following declarations are valid and invalid.
+
+.. code-block:: c++
+
+  struct Foo {
+    [[clang::designated_init_required]] int x;
+  };
+
+  Foo foo; // invalid
+  Foo foo {}; // invalid
+  Foo foo {1}; // invalid
+  Foo foo {.x = 1}; // valid
+  }];
+}
+
 def WarnMaybeUnusedDocs : Documentation {
   let Category = DocCatVariable;
   let Heading = "maybe_unused, unused";
@@ -4194,4 +4249,4 @@
 not initialized on device side. It has internal linkage and is initialized by
 the initializer on host side.
   }];
-}
\ No newline at end of file
+}
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -1944,6 +1944,18 @@
   let LangOpts = [CPlusPlus];
 }
 
+def RequireDesignatedInit : InheritableAttr {
+  let Spellings = [CXX11<"clang", "require_designated_init">];
+  let Subjects = SubjectList<[Type]>;
+  let Documentation = [RequireDesignatedInitDocs];
+}
+
+def Required : InheritableAttr {
+  let Spellings = [CXX11<"clang", "designated_init_required">];
+  let Subjects = SubjectList<[Field]>;
+  let Documentation = [RequiredDocs];
+}
+
 def WorkGroupSizeHint :  InheritableAttr {
   // Does not have a [[]] spelling because it is an OpenCL-related attribute.
   let Spellings = [GNU<"work_group_size_hint">];
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to