EricWF updated this revision to Diff 69775.
EricWF added a comment.

Make the attribute C++ only.


https://reviews.llvm.org/D23385

Files:
  include/clang/Basic/Attr.td
  include/clang/Basic/AttrDocs.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/AttributeList.h
  lib/Sema/SemaDecl.cpp
  lib/Sema/SemaDeclAttr.cpp
  test/SemaCXX/attr-require-constant-initialization.cpp
  utils/TableGen/ClangAttrEmitter.cpp

Index: utils/TableGen/ClangAttrEmitter.cpp
===================================================================
--- utils/TableGen/ClangAttrEmitter.cpp
+++ utils/TableGen/ClangAttrEmitter.cpp
@@ -2790,8 +2790,10 @@
   std::string FnName = "check", Test;
   for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I) {
     std::string Part = (*I)->getValueAsString("Name");
-    if ((*I)->getValueAsBit("Negated"))
+    if ((*I)->getValueAsBit("Negated")) {
+      FnName += "Not";
       Test += "!";
+    }
     Test += "S.LangOpts." + Part;
     if (I + 1 != E)
       Test += " || ";
Index: test/SemaCXX/attr-require-constant-initialization.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/attr-require-constant-initialization.cpp
@@ -0,0 +1,282 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -DTEST_ONE -std=c++03 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -DTEST_ONE -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -DTEST_ONE -std=c++14 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -DTEST_TWO \
+// RUN: -Wglobal-constructors -std=c++14 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -DTEST_THREE -xc %s
+
+#define ATTR __attribute__((require_constant_initialization)) // expected-note 0+ {{expanded from macro}}
+
+int ReturnInt();
+
+struct PODType {
+  int value;
+  int value2;
+};
+
+#if defined(__cplusplus)
+
+#if __cplusplus >= 201103L
+struct LitType {
+  constexpr LitType() : value(0) {}
+  constexpr LitType(int x) : value(x) {}
+  LitType(void *) : value(-1) {}
+  int value;
+};
+#endif
+
+struct NonLit {
+#if __cplusplus >= 201402L
+  constexpr NonLit() : value(0) {}
+  constexpr NonLit(int x) : value(x) {}
+#else
+  NonLit() : value(0) {}
+  NonLit(int x) : value(x) {}
+#endif
+  NonLit(void *) : value(-1) {}
+  ~NonLit() {}
+  int value;
+};
+
+struct StoresNonLit {
+#if __cplusplus >= 201402L
+  constexpr StoresNonLit() : obj() {}
+  constexpr StoresNonLit(int x) : obj(x) {}
+#else
+  StoresNonLit() : obj() {}
+  StoresNonLit(int x) : obj(x) {}
+#endif
+  StoresNonLit(void *p) : obj(p) {}
+  NonLit obj;
+};
+
+#endif // __cplusplus
+
+
+#if defined(TEST_ONE) // Test semantics of attribute
+
+// Test diagnostics when attribute is applied to non-static declarations.
+void test_func_local(ATTR int param) { // expected-error {{only applies to variables with static or thread}}
+  ATTR int x = 42;                     // expected-error {{only applies to variables with static or thread}}
+  ATTR extern int y;
+}
+struct ATTR class_mem { // expected-error {{only applies to variables with static or thread}}
+  ATTR int x;           // expected-error {{only applies to variables with static or thread}}
+};
+
+// [basic.start.static]p2.1
+// if each full-expression (including implicit conversions) that appears in
+// the initializer of a reference with static or thread storage duration is
+// a constant expression (5.20) and the reference is bound to a glvalue
+// designating an object with static storage duration, to a temporary object
+// (see 12.2) or subobject thereof, or to a function;
+
+// Test binding to a static glvalue
+const int glvalue_int = 42;
+const int glvalue_int2 = ReturnInt();
+ATTR const int &glvalue_ref ATTR = glvalue_int;
+ATTR const int &glvalue_ref2 ATTR = glvalue_int2;
+ATTR __thread const int &glvalue_ref_tl = glvalue_int;
+
+void test_basic_start_static_2_1() {
+  const int non_global = 42;
+  ATTR static const int &local_init = non_global; // expected-error {{variable does not have a constant initializer}}
+  // expected-note@-1 {{required by 'require_constant_initializer' attribute here}}
+  ATTR static const int &global_init = glvalue_int;
+  ATTR static const int &temp_init = 42;
+}
+
+ATTR const int &temp_ref = 42;
+ATTR const int &temp_ref2 = ReturnInt(); // expected-error {{variable does not have a constant initializer}}
+// expected-note@-1 {{required by 'require_constant_initializer' attribute here}}
+ATTR const NonLit &nl_temp_ref = 42; // expected-error {{variable does not have a constant initializer}}
+// expected-note@-1 {{required by 'require_constant_initializer' attribute here}}
+
+#if __cplusplus >= 201103L
+ATTR const LitType &lit_temp_ref = 42;
+ATTR const int &subobj_ref = LitType{}.value;
+#endif
+
+ATTR const int &nl_subobj_ref = NonLit().value; // expected-error {{variable does not have a constant initializer}}
+// expected-note@-1 {{required by 'require_constant_initializer' attribute here}}
+
+struct TT1 {
+  ATTR static const int &no_init;
+  ATTR static const int &glvalue_init;
+  ATTR static const int &temp_init;
+  ATTR static const int &subobj_init;
+#if __cplusplus >= 201103L
+  ATTR static thread_local const int &tl_glvalue_init;
+  ATTR static thread_local const int &tl_temp_init; // expected-note {{required by 'require_constant_initializer' attribute here}}
+#endif
+};
+const int &TT1::glvalue_init = glvalue_int;
+const int &TT1::temp_init = 42;
+const int &TT1::subobj_init = PODType().value;
+#if __cplusplus >= 201103L
+thread_local const int &TT1::tl_glvalue_init = glvalue_int;
+thread_local const int &TT1::tl_temp_init = 42; // expected-error {{variable does not have a constant initializer}}
+#endif
+
+// [basic.start.static]p2.2
+// if an object with static or thread storage duration is initialized by a
+// constructor call, and if the initialization full-expression is a constant
+// initializer for the object;
+
+void test_basic_start_static_2_2() {
+#if __cplusplus < 201103L
+  ATTR static PODType pod;
+#else
+  ATTR static PODType pod; // expected-error {{variable does not have a constant initializer}}
+// expected-note@-1 {{required by 'require_constant_initializer' attribute here}}
+#endif
+  ATTR static PODType pot2 = {ReturnInt()}; // expected-error {{variable does not have a constant initializer}}
+                                            // expected-note@-1 {{required by 'require_constant_initializer' attribute here}}
+
+#if __cplusplus >= 201103L
+  constexpr LitType l;
+  ATTR static LitType static_lit = l;
+  ATTR static LitType static_lit2 = (void *)0; // expected-error {{variable does not have a constant initializer}}
+  // expected-note@-1 {{required by 'require_constant_initializer' attribute here}}
+  ATTR static LitType static_lit3 = ReturnInt(); // expected-error {{variable does not have a constant initializer}}
+  // expected-note@-1 {{required by 'require_constant_initializer' attribute here}}
+  ATTR thread_local LitType tls = 42;
+#endif
+}
+
+struct TT2 {
+  ATTR static PODType pod_noinit;
+#if __cplusplus >= 201103L
+// expected-note@-2 {{required by 'require_constant_initializer' attribute here}}
+#endif
+  ATTR static PODType pod_copy_init; // expected-note {{required by 'require_constant_initializer' attribute here}}
+#if __cplusplus >= 201402L
+  ATTR static constexpr LitType lit = {};
+  ATTR static const NonLit non_lit;
+  ATTR static const NonLit non_lit_list_init;
+  ATTR static const NonLit non_lit_copy_init; // expected-note {{required by 'require_constant_initializer' attribute here}}
+#endif
+};
+PODType TT2::pod_noinit;
+#if __cplusplus >= 201103L
+// expected-error@-2 {{variable does not have a constant initializer}}
+#endif
+PODType TT2::pod_copy_init(TT2::pod_noinit); // expected-error {{variable does not have a constant initializer}}
+#if __cplusplus >= 201402L
+const NonLit TT2::non_lit(42);
+const NonLit TT2::non_lit_list_init = {42};
+const NonLit TT2::non_lit_copy_init = 42; // expected-error {{variable does not have a constant initializer}}
+#endif
+
+#if __cplusplus >= 201103L
+ATTR LitType lit_ctor;
+ATTR LitType lit_ctor2{};
+ATTR LitType lit_ctor3 = {};
+ATTR __thread LitType lit_ctor_tl = {};
+
+#if __cplusplus >= 201402L
+ATTR NonLit nl_ctor;
+ATTR NonLit nl_ctor2{};
+ATTR NonLit nl_ctor3 = {};
+ATTR thread_local NonLit nl_ctor_tl = {};
+ATTR StoresNonLit snl;
+#else
+ATTR NonLit nl_ctor; // expected-error {{variable does not have a constant initializer}}
+// expected-note@-1 {{required by 'require_constant_initializer' attribute here}}
+ATTR NonLit nl_ctor2{}; // expected-error {{variable does not have a constant initializer}}
+// expected-note@-1 {{required by 'require_constant_initializer' attribute here}}
+ATTR NonLit nl_ctor3 = {}; // expected-error {{variable does not have a constant initializer}}
+// expected-note@-1 {{required by 'require_constant_initializer' attribute here}}
+ATTR thread_local NonLit nl_ctor_tl = {}; // expected-error {{variable does not have a constant initializer}}
+// expected-note@-1 {{required by 'require_constant_initializer' attribute here}}
+ATTR StoresNonLit snl; // expected-error {{variable does not have a constant initializer}}
+// expected-note@-1 {{required by 'require_constant_initializer' attribute here}}
+#endif
+
+// Non-literal types cannot appear in the initializer of a non-literal type.
+ATTR int nl_in_init = NonLit{42}.value; // expected-error {{variable does not have a constant initializer}}
+// expected-note@-1 {{required by 'require_constant_initializer' attribute here}}
+ATTR int lit_in_init = LitType{42}.value;
+#endif
+
+// [basic.start.static]p2.3
+// if an object with static or thread storage duration is not initialized by a
+// constructor call and if either the object is value-initialized or every
+// full-expression that appears in its initializer is a constant expression.
+void test_basic_start_static_2_3() {
+  ATTR static int static_local = 42;
+  ATTR static int static_local2; // zero-initialization takes place
+#if __cplusplus >= 201103L
+  ATTR thread_local int tl_local = 42;
+#endif
+}
+
+ATTR int no_init; // zero initialization takes place
+ATTR int arg_init = 42;
+ATTR PODType pod_init = {};
+ATTR PODType pod_missing_init = {42 /* should have second arg */};
+ATTR PODType pod_full_init = {1, 2};
+ATTR PODType pod_non_constexpr_init = {1, ReturnInt()}; // expected-error {{variable does not have a constant initializer}}
+// expected-note@-1 {{required by 'require_constant_initializer' attribute here}}
+
+#if __cplusplus >= 201103L
+ATTR int val_init{};
+ATTR int brace_init = {};
+#endif
+
+ATTR __thread int tl_init = 0;
+typedef const char *StrType;
+
+#if __cplusplus >= 201103L
+
+// Test that the validity of the selected constructor is checked, not just the
+// initializer
+struct NotC {
+  constexpr NotC(void *) {}
+  NotC(int) {}
+};
+template <class T>
+struct TestCtor {
+  constexpr TestCtor(int x) : value(x) {}
+  T value;
+};
+ATTR TestCtor<NotC> t(42); // expected-error {{variable does not have a constant initializer}}
+// expected-note@-1 {{required by 'require_constant_initializer' attribute here}}
+#endif
+
+// Test various array types
+ATTR const char *foo[] = {"abc", "def"};
+ATTR PODType bar[] = {{}, {123, 456}};
+
+#elif defined(TEST_TWO) // Test for duplicate warnings
+struct NotC {
+  constexpr NotC(void *) {}
+  NotC(int) {} // expected-note 2 {{declared here}}
+};
+template <class T>
+struct TestCtor {
+  constexpr TestCtor(int x) : value(x) {} // expected-note 2 {{non-constexpr constructor}}
+  T value;
+};
+
+ATTR LitType non_const_lit(nullptr); // expected-error {{variable does not have a constant initializer}}
+// expected-note@-1 {{required by 'require_constant_initializer' attribute here}}
+ATTR NonLit non_const(nullptr); // expected-error {{variable does not have a constant initializer}}
+// expected-warning@-1 {{declaration requires a global destructor}}
+// expected-note@-2 {{required by 'require_constant_initializer' attribute here}}
+LitType const_init_lit(nullptr);              // expected-warning {{declaration requires a global constructor}}
+NonLit const_init{42};                        // expected-warning {{declaration requires a global destructor}}
+constexpr TestCtor<NotC> inval_constexpr(42); // expected-error {{must be initialized by a constant expression}}
+// expected-note@-1 {{in call to 'TestCtor(42)'}}
+ATTR constexpr TestCtor<NotC> inval_constexpr2(42); // expected-error {{must be initialized by a constant expression}}
+// expected-note@-1 {{in call to 'TestCtor(42)'}}
+
+#elif defined(TEST_THREE)
+#if defined(__cplusplus)
+#error This test requires C
+#endif
+// Test that using the attribute in C results in a diagnostic
+ATTR int x = 0; // expected-warning {{attribute ignored}}
+#else
+#error No test case specified
+#endif // defined(TEST_N)
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp
+++ lib/Sema/SemaDeclAttr.cpp
@@ -5630,6 +5630,9 @@
   case AttributeList::AT_VecTypeHint:
     handleVecTypeHint(S, D, Attr);
     break;
+  case AttributeList::AT_RequireConstantInit:
+    handleSimpleAttribute<RequireConstantInitAttr>(S, D, Attr);
+    break;
   case AttributeList::AT_InitPriority:
     handleInitPriorityAttr(S, D, Attr);
     break;
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -10392,26 +10392,35 @@
       Diag(var->getLocation(), diag::warn_missing_variable_declarations) << var;
   }
 
+  // Cache the result of checking for constant initialization.
+  Optional<bool> CacheHasConstInit;
+  const Expr *CacheCulprit;
+  auto checkConstInit = [&]() mutable {
+    if (!CacheHasConstInit)
+      CacheHasConstInit = var->getInit()->isConstantInitializer(
+            Context, var->getType()->isReferenceType(), &CacheCulprit);
+    return *CacheHasConstInit;
+  };
+
   if (var->getTLSKind() == VarDecl::TLS_Static) {
-    const Expr *Culprit;
     if (var->getType().isDestructedType()) {
       // GNU C++98 edits for __thread, [basic.start.term]p3:
       //   The type of an object with thread storage duration shall not
       //   have a non-trivial destructor.
       Diag(var->getLocation(), diag::err_thread_nontrivial_dtor);
       if (getLangOpts().CPlusPlus11)
         Diag(var->getLocation(), diag::note_use_thread_local);
-    } else if (getLangOpts().CPlusPlus && var->hasInit() &&
-               !var->getInit()->isConstantInitializer(
-                   Context, var->getType()->isReferenceType(), &Culprit)) {
-      // GNU C++98 edits for __thread, [basic.start.init]p4:
-      //   An object of thread storage duration shall not require dynamic
-      //   initialization.
-      // FIXME: Need strict checking here.
-      Diag(Culprit->getExprLoc(), diag::err_thread_dynamic_init)
-        << Culprit->getSourceRange();
-      if (getLangOpts().CPlusPlus11)
-        Diag(var->getLocation(), diag::note_use_thread_local);
+    } else if (getLangOpts().CPlusPlus && var->hasInit()) {
+      if (!checkConstInit()) {
+        // GNU C++98 edits for __thread, [basic.start.init]p4:
+        //   An object of thread storage duration shall not require dynamic
+        //   initialization.
+        // FIXME: Need strict checking here.
+        Diag(CacheCulprit->getExprLoc(), diag::err_thread_dynamic_init)
+          << CacheCulprit->getSourceRange();
+        if (getLangOpts().CPlusPlus11)
+          Diag(var->getLocation(), diag::note_use_thread_local);
+      }
     }
   }
 
@@ -10485,18 +10494,6 @@
 
   if (!var->getDeclContext()->isDependentContext() &&
       Init && !Init->isValueDependent()) {
-    if (IsGlobal && !var->isConstexpr() &&
-        !getDiagnostics().isIgnored(diag::warn_global_constructor,
-                                    var->getLocation())) {
-      // Warn about globals which don't have a constant initializer.  Don't
-      // warn about globals with a non-trivial destructor because we already
-      // warned about them.
-      CXXRecordDecl *RD = baseType->getAsCXXRecordDecl();
-      if (!(RD && !RD->hasTrivialDestructor()) &&
-          !Init->isConstantInitializer(Context, baseType->isReferenceType()))
-        Diag(var->getLocation(), diag::warn_global_constructor)
-          << Init->getSourceRange();
-    }
 
     if (var->isConstexpr()) {
       SmallVector<PartialDiagnosticAt, 8> Notes;
@@ -10520,6 +10517,35 @@
       // initialized by a constant expression if we check later.
       var->checkInitIsICE();
     }
+
+    // Don't emit further diagnostics about constexpr globals since they
+    // were just diagnosed.
+    if (!var->isConstexpr() && GlobalStorage &&
+            var->hasAttr<RequireConstantInitAttr>()) {
+      // FIXME: Need strict checking in C++03 here.
+      bool DiagErr = getLangOpts().CPlusPlus11
+          ? !var->checkInitIsICE() : !checkConstInit();
+      if (DiagErr) {
+        auto attr = var->getAttr<RequireConstantInitAttr>();
+        Diag(var->getLocation(), diag::err_require_constant_init_failed)
+          << Init->getSourceRange();
+        Diag(attr->getLocation(), diag::note_declared_required_constant_init_here)
+          << attr->getRange();
+      }
+    }
+    else if (!var->isConstexpr() && IsGlobal &&
+             !getDiagnostics().isIgnored(diag::warn_global_constructor,
+                                    var->getLocation())) {
+      // Warn about globals which don't have a constant initializer.  Don't
+      // warn about globals with a non-trivial destructor because we already
+      // warned about them.
+      CXXRecordDecl *RD = baseType->getAsCXXRecordDecl();
+      if (!(RD && !RD->hasTrivialDestructor())) {
+        if (!checkConstInit())
+          Diag(var->getLocation(), diag::warn_global_constructor)
+            << Init->getSourceRange();
+      }
+    }
   }
 
   // Require the destructor.
Index: include/clang/Sema/AttributeList.h
===================================================================
--- include/clang/Sema/AttributeList.h
+++ include/clang/Sema/AttributeList.h
@@ -897,6 +897,7 @@
   ExpectedFunctionVariableOrClass,
   ExpectedFunctionVariableClassOrObjCInterface,
   ExpectedObjectiveCProtocol,
+  ExpectedStaticOrTLSVar,
   ExpectedFunctionGlobalVarMethodOrProperty,
   ExpectedStructOrUnionOrTypedef,
   ExpectedStructOrTypedef,
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -2572,7 +2572,7 @@
   "Objective-C instance methods|init methods of interface or class extension declarations|"
   "variables, functions and classes|"
   "functions, variables, classes, and Objective-C interfaces|"
-  "Objective-C protocols|"
+  "Objective-C protocols|variables with static or thread storage duration|"
   "functions and global variables|structs, unions, and typedefs|structs and typedefs|"
   "interface or protocol declarations|kernel functions|non-K&R-style functions|"
   "variables, enums, fields and typedefs|functions, methods, enums, and classes|"
@@ -6843,7 +6843,12 @@
 
 def err_incomplete_type_used_in_type_trait_expr : Error<
   "incomplete type %0 used in type trait expression">;
-  
+
+def err_require_constant_init_failed : Error<
+  "variable does not have a constant initializer">;
+def note_declared_required_constant_init_here : Note<
+  "required by 'require_constant_initializer' attribute here">;
+
 def err_dimension_expr_not_constant_integer : Error<
   "dimension expression does not evaluate to a constant unsigned int">;
 
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -829,6 +829,43 @@
   }];
 }
 
+
+def RequireConstantInitDocs : Documentation {
+  let Category = DocCatVariable;
+  let Content = [{
+This attribute specifies that the variable to which it is attached is intended
+to have a `constant initializer <http://en.cppreference.com/w/cpp/language/constant_initialization>`_
+according to the rules of [basic.start.static]. The variable is required to
+have static or thread storage duration. If the initialization of the variable
+is not a constant initializer, an error will be produced. This attribute may
+only be used in C++.
+
+Note that in C++03 strict constant expression checking is not done. Instead
+the attribute reports if Clang can emit the the variable as a constant, even
+if it's not technically a 'constant initializer'. This behavior is non-portable.
+
+Static storage duration variables with constant initializers avoid hard-to-find
+bugs caused by the indeterminate order of dynamic initialization. They can also
+be safely used during dynamic initialization across translation units.
+
+This attribute acts as a compile time assertion that the requirements
+for constant initialization have been met. Since these requirements change
+between dialects and have subtle pitfalls it's important to fail fast instead
+of silently falling back on dynamic initialization.
+
+.. code-block:: c++
+  // -std=c++14
+  #define SAFE_STATIC __attribute__((require_constant_initialization)) static
+  struct T {
+    constexpr T(int) {}
+    ~T(); // non-trivial
+  };
+  SAFE_STATIC T x = {42}; // Initialization OK. Doesn't check destructor.
+  SAFE_STATIC T y = 42; // error: variable does not have a constant initializer
+  // copy initialization is not a constant expression on a non-literal type.
+  }];
+}
+
 def WarnMaybeUnusedDocs : Documentation {
   let Category = DocCatVariable;
   let Heading = "maybe_unused, unused, gnu::unused";
@@ -845,12 +882,12 @@
 enumerator, a non-static data member, or a label.
 
 .. code-block: c++
-  #include <cassert>
-
-  [[maybe_unused]] void f([[maybe_unused]] bool thing1,
-                          [[maybe_unused]] bool thing2) {
-    [[maybe_unused]] bool b = thing1 && thing2;
-    assert(b);
+  #include <cassert>
+
+  [[maybe_unused]] void f([[maybe_unused]] bool thing1,
+                          [[maybe_unused]] bool thing2) {
+    [[maybe_unused]] bool b = thing1 && thing2;
+    assert(b);
   }
   }];
 }
@@ -867,15 +904,15 @@
 `void`.
 
 .. code-block: c++
-  struct [[nodiscard]] error_info { /*...*/ };
-  error_info enable_missile_safety_mode();
-  
-  void launch_missiles();
-  void test_missiles() {
-    enable_missile_safety_mode(); // diagnoses
-    launch_missiles();
-  }
-  error_info &foo();
+  struct [[nodiscard]] error_info { /*...*/ };
+  error_info enable_missile_safety_mode();
+
+  void launch_missiles();
+  void test_missiles() {
+    enable_missile_safety_mode(); // diagnoses
+    launch_missiles();
+  }
+  error_info &foo();
   void f() { foo(); } // Does not diagnose, error_info is a reference.  
   }];
 }
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -241,6 +241,7 @@
 def Borland : LangOpt<"Borland">;
 def CUDA : LangOpt<"CUDA">;
 def COnly : LangOpt<"CPlusPlus", 1>;
+def CPlusPlus : LangOpt<"CPlusPlus">;
 def OpenCL : LangOpt<"OpenCL">;
 def RenderScript : LangOpt<"RenderScript">;
 
@@ -1380,6 +1381,15 @@
   let Documentation = [Undocumented];
 }
 
+def RequireConstantInit : InheritableAttr {
+  let Spellings = [GNU<"require_constant_initialization">,
+                   CXX11<"clang", "require_constant_initialization">];
+  let Subjects = SubjectList<[GlobalVar], ErrorDiag,
+                              "ExpectedStaticOrTLSVar">;
+  let Documentation = [RequireConstantInitDocs];
+  let LangOpts = [CPlusPlus];
+}
+
 def WorkGroupSizeHint :  InheritableAttr {
   let Spellings = [GNU<"work_group_size_hint">];
   let Args = [UnsignedArgument<"XDim">, 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to