emmettneyman updated this revision to Diff 209097.
emmettneyman marked 32 inline comments as done.
emmettneyman added a comment.

Addressed several of the comments regarding naming and style.


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-requires-designator.cpp
  clang/test/SemaCXX/attr-requires-init.cpp

Index: clang/test/SemaCXX/attr-requires-init.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/attr-requires-init.cpp
@@ -0,0 +1,61 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+[[clang::requires_init]] int x;            // expected-warning{{'requires_init' attribute only applies to non-static data members}}
+[[clang::requires_init]] void fun(int x) { // expected-warning{{'requires_init' attribute only applies to non-static data members}}
+  return;
+}
+struct [[clang::requires_init]] Foo { // expected-warning{{'requires_init' attribute only applies to non-static data members}}
+  int x;
+};
+
+// Struct with one required field
+struct Bar {
+  [[clang::requires_init]] int y; // expected-note 0+ {{enforced by 'requires_init' attribute here}}
+};
+
+// The following are invalid ways of initializing instances of this struct.
+Bar b1;    // expected-warning{{initializer for variable b1 must explicitly initialize field y}}
+Bar b2{};  // expected-warning{{initializer for variable b2 must explicitly initialize field y}}
+Bar b3{1}; // expected-warning{{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 {
+  [[clang::requires_init]] int x; // expected-note 0+ {{enforced by 'requires_init' attribute here}}
+  int y;
+  [[clang::requires_init]] int z; // expected-note 0+ {{enforced by 'requires_init' attribute here}}
+};
+
+// The following are invalid ways of initializing instances of this struct.
+Baz z1;                 // expected-warning{{initializer for variable z1 must explicitly initialize field x}} expected-warning{{initializer for variable z1 must explicitly initialize field z}}
+Baz z2{};               // expected-warning{{initializer for variable z2 must explicitly initialize field x}} expected-warning{{initializer for variable z2 must explicitly initialize field z}}
+Baz z3{1, 2};           // expected-warning{{initializer for variable z3 must explicitly initialize field x}} expected-warning{{initializer for variable z3 must explicitly initialize field z}}
+Baz z4{1, 2, 3};        // expected-warning{{initializer for variable z4 must explicitly initialize field x}} expected-warning{{initializer for variable z4 must explicitly initialize field z}}
+Baz z5{.x = 1, 2};      // expected-warning{{initializer for variable z5 must explicitly initialize field z}}
+Baz z6{.x = 1, .y = 2}; // expected-warning{{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:
+  [[clang::requires_init]] int x; // expected-note 0+ {{enforced by 'requires_init' attribute here}}
+  int y;
+};
+
+// The following are invalid ways of initializing instances of this class.
+Cla c1;            // expected-warning{{initializer for variable c1 must explicitly initialize field x}}
+Cla c2{};          // expected-warning{{initializer for variable c2 must explicitly initialize field x}}
+Cla c3{1};         // expected-warning{{initializer for variable c3 must explicitly initialize field x}}
+Cla c4{1, 2};      // expected-warning{{initializer for variable c4 must explicitly initialize field x}}
+Cla c5{1, .y = 2}; // expected-warning{{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/SemaCXX/attr-requires-designator.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/attr-requires-designator.cpp
@@ -0,0 +1,102 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+// The requires_designator attribute only applies to types. It will
+// generate a warning when attached to variables, functions, arrays, etc.
+[[clang::requires_designator]] int x;            // expected-warning{{'requires_designator' attribute only applies to structs, unions, and classes}}
+[[clang::requires_designator]] void fun(int x) { // expected-warning{{'requires_designator' attribute only applies to structs, unions, and classes}}
+  return;
+}
+[[clang::requires_designator]] int arr[10]; // expected-warning{{'requires_designator' attribute only applies to structs, unions, and classes}}
+
+// Struct with one field with requires_designator attribute
+struct [[clang::requires_designator]] Foo { // expected-note 0+ {{required by 'requires_designator' attribute here}}
+  int a;
+};
+
+// The following are invalid ways of initializing instances of this struct.
+Foo f1;       // expected-warning{{variable declaration does not use designated initializer syntax}}
+Foo f2{1};    // expected-warning{{variable declaration does not use designated initializer syntax}}
+Foo f3 = {1}; // expected-warning{{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 requires_designator attribute
+struct [[clang::requires_designator]] Bar { // expected-note 0+ {{required by 'requires_designator' attribute here}}
+  int b;
+  int c;
+};
+
+// The following are invalid ways of initializing instances of this struct.
+Bar b1;               // expected-warning{{variable declaration does not use designated initializer syntax}}
+Bar b2{1, 2};         // expected-warning{{variable declaration does not use designated initializer syntax}}
+Bar b3 = {1, 2};      // expected-warning{{variable declaration does not use designated initializer syntax}}
+Bar b4{.b = 1, 2};    // expected-warning{{variable declaration does not use designated initializer syntax}}
+Bar b5 = {.b = 1, 2}; // expected-warning{{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 requires_designator 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 requires_designator attribute can also be attached to unions.
+union [[clang::requires_designator]] Uni { // expected-note 0+ {{required by 'requires_designator' attribute here}}
+  int x;
+  int y;
+};
+
+// The following are invalid ways of initializing instances of this union.
+Uni u1;       // expected-warning{{variable declaration does not use designated initializer syntax}}
+Uni u2{1};    // expected-warning{{variable declaration does not use designated initializer syntax}}
+Uni u3 = {1}; // expected-warning{{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 requires_designator attribute can also be attached to classes.
+class [[clang::requires_designator]] Cla { // expected-note 0+ {{required by 'requires_designator' attribute here}}
+public:
+  int x;
+  int y;
+};
+
+// The following are invalid ways of initializing instances of this class.
+Cla c1;               // expected-warning{{variable declaration does not use designated initializer syntax}}
+Cla c2{1, 2};         // expected-warning{{variable declaration does not use designated initializer syntax}}
+Cla c3 = {1, 2};      // expected-warning{{variable declaration does not use designated initializer syntax}}
+Cla c4{.x = 1, 2};    // expected-warning{{variable declaration does not use designated initializer syntax}}
+Cla c5 = {.x = 1, 2}; // expected-warning{{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/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,8 @@
 // CHECK-NEXT: RenderScriptKernel (SubjectMatchRule_function)
 // CHECK-NEXT: ReqdWorkGroupSize (SubjectMatchRule_function)
 // CHECK-NEXT: RequireConstantInit (SubjectMatchRule_variable_is_global)
+// CHECK-NEXT: RequiresDesignator (SubjectMatchRule_record)
+// CHECK-NEXT: RequiresInit (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
@@ -6959,6 +6959,12 @@
   case ParsedAttr::AT_RequireConstantInit:
     handleSimpleAttribute<RequireConstantInitAttr>(S, D, AL);
     break;
+  case ParsedAttr::AT_RequiresDesignator:
+    handleSimpleAttribute<RequiresDesignatorAttr>(S, D, AL);
+    break;
+  case ParsedAttr::AT_RequiresInit:
+    handleSimpleAttribute<RequiresInitAttr>(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,79 @@
     Init = Result.get();
   }
 
+  if (const 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 (const auto *RAttr = TD->getAttr<RequiresDesignatorAttr>()) {
+      if (const auto *ILE = dyn_cast<InitListExpr>(Init)) {
+        for (auto *I : ILE->inits()) {
+          if (auto *DIE = dyn_cast<DesignatedInitExpr>(I))
+            continue;
+          SourceRange SR(VDecl->getSourceRange().getBegin(),
+                         Init->getSourceRange().getEnd());
+          Diag(I->getExprLoc(), diag::warn_requires_designator_failed)
+              << SR;
+          Diag(RAttr->getLocation(),
+               diag::note_declared_requires_designator_here)
+              << RAttr->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 (const auto *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<RequiresInitAttr>()) {
+          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 (const auto *ILE = dyn_cast<InitListExpr>(Init)) {
+          for (auto *I : ILE->inits()) {
+            if (auto *DIE = dyn_cast<DesignatedInitExpr>(I)) {
+              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 (const auto FD : RD->fields()) {
+          if (RequiredFields.count(FD->getIdentifier()) != 0) {
+            SourceRange SR(VDecl->getSourceRange().getBegin(),
+                           Init->getSourceRange().getEnd());
+            Diag(Init->getExprLoc(), diag::warn_requires_init_failed)
+                << SR << VDecl->getName() << FD->getName();
+            const auto *A = FD->getAttr<RequiresInitAttr>();
+            Diag(A->getLocation(), diag::note_declared_requires_init_here)
+                << A->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 +11625,37 @@
   if (VarDecl *Var = dyn_cast<VarDecl>(RealDecl)) {
     QualType Type = Var->getType();
 
+    if (const auto *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 (const auto *A = TD->getAttr<RequiresDesignatorAttr>()) {
+        Diag(Var->getLocation(), diag::warn_requires_designator_failed)
+            << Var->getSourceRange();
+        Diag(A->getLocation(),
+             diag::note_declared_requires_designator_here)
+            << A->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 (const auto *RD = dyn_cast<RecordDecl>(TD)) {
+        for (const auto *FD : RD->fields()) {
+          if (const auto *A = FD->getAttr<RequiresInitAttr>()) {
+            Diag(Var->getLocation(), diag::warn_requires_init_failed)
+                << Var->getSourceRange() << Var->getName() << FD->getName();
+            Diag(A->getLocation(), diag::note_declared_requires_init_here)
+                << A->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 warn_requires_designator_failed : Warning<
+  "variable declaration does not use designated initializer syntax">;
+def note_declared_requires_designator_here : Note<
+  "required by 'requires_designator' attribute here">;
+
+def warn_requires_init_failed : Warning<
+  "initializer for variable %0 must explicitly initialize field %1">;
+def note_declared_requires_init_here : Note<
+  "enforced by 'requires_init' 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,60 @@
   }];
 }
 
+def RequiresDesignatorDocs : Documentation {
+  let Category = DocCatType;
+  let Content = [{
+This attribute can be applied to a struct definition to require that any time a
+variable of that structure'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; // invalid
+  }];
+}
+
+def RequiresInitDocs : Documentation {
+  let Category = DocCatType;
+  let Content = [{
+This attribute can be applied to a field definition within a struct or a class
+to require that any time a variable of the struct'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::requires_init]] 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 +4248,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 RequiresDesignator : InheritableAttr {
+  let Spellings = [Clang<"requires_designator">];
+  let Subjects = SubjectList<[Record]>;
+  let Documentation = [RequiresDesignatorDocs];
+}
+
+def RequiresInit : InheritableAttr {
+  let Spellings = [Clang<"requires_init">];
+  let Subjects = SubjectList<[Field]>;
+  let Documentation = [RequiresInitDocs];
+}
+
 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