emmettneyman updated this revision to Diff 208840.
emmettneyman marked an inline comment as done.
emmettneyman added a comment.
Remove cppreference link, add reference to C++ spec.
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]/3.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]/3.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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits