r280525 - Implement __attribute__((require_constant_initialization)) for safe static initialization.

Eric Fiselier via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 2 11:53:32 PDT 2016


Author: ericwf
Date: Fri Sep  2 13:53:31 2016
New Revision: 280525

URL: http://llvm.org/viewvc/llvm-project?rev=280525&view=rev
Log:
Implement __attribute__((require_constant_initialization)) for safe static initialization.

Summary:
This attribute specifies expectations about the initialization of static and
thread local variables. Specifically that the variable has a
[constant initializer](http://en.cppreference.com/w/cpp/language/constant_initialization)
according to the rules of [basic.start.static]. Failure to meet this expectation
will result in an error.

Static objects with constant initializers avoid hard-to-find bugs caused by
the indeterminate order of dynamic initialization. They can also be safely
used by other static constructors 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.

```c++
  // -std=c++14
  #define SAFE_STATIC __attribute__((require_constant_initialization)) static
  struct T {
    constexpr T(int) {}
    ~T();
  };
  SAFE_STATIC T x = {42}; // OK.
  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.
```
This attribute can only be applied to objects with static or thread-local storage
duration.

Reviewers: majnemer, rsmith, aaron.ballman

Subscribers: jroelofs, cfe-commits

Differential Revision: https://reviews.llvm.org/D23385

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

Modified: cfe/trunk/include/clang/Basic/Attr.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=280525&r1=280524&r2=280525&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Fri Sep  2 13:53:31 2016
@@ -241,6 +241,7 @@ def MicrosoftExt : LangOpt<"MicrosoftExt
 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 @@ def ReqdWorkGroupSize : InheritableAttr
   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">, 

Modified: cfe/trunk/include/clang/Basic/AttrDocs.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/AttrDocs.td?rev=280525&r1=280524&r2=280525&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/AttrDocs.td (original)
+++ cfe/trunk/include/clang/Basic/AttrDocs.td Fri Sep  2 13:53:31 2016
@@ -829,6 +829,43 @@ When one method overrides another, the o
   }];
 }
 
+
+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";

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=280525&r1=280524&r2=280525&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Sep  2 13:53:31 2016
@@ -2572,7 +2572,7 @@ def warn_attribute_wrong_decl_type : War
   "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|"
@@ -6839,7 +6839,12 @@ def note_inequality_comparison_to_or_ass
 
 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">;
 

Modified: cfe/trunk/include/clang/Sema/AttributeList.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/AttributeList.h?rev=280525&r1=280524&r2=280525&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/AttributeList.h (original)
+++ cfe/trunk/include/clang/Sema/AttributeList.h Fri Sep  2 13:53:31 2016
@@ -897,6 +897,7 @@ enum AttributeDeclKind {
   ExpectedFunctionVariableOrClass,
   ExpectedFunctionVariableClassOrObjCInterface,
   ExpectedObjectiveCProtocol,
+  ExpectedStaticOrTLSVar,
   ExpectedFunctionGlobalVarMethodOrProperty,
   ExpectedStructOrUnionOrTypedef,
   ExpectedStructOrTypedef,

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=280525&r1=280524&r2=280525&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Fri Sep  2 13:53:31 2016
@@ -10393,8 +10393,17 @@ void Sema::CheckCompleteVariableDeclarat
       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
@@ -10402,17 +10411,17 @@ void Sema::CheckCompleteVariableDeclarat
       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);
+      }
     }
   }
 
@@ -10486,18 +10495,6 @@ void Sema::CheckCompleteVariableDeclarat
 
   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;
@@ -10521,6 +10518,35 @@ void Sema::CheckCompleteVariableDeclarat
       // 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.

Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=280525&r1=280524&r2=280525&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Fri Sep  2 13:53:31 2016
@@ -5630,6 +5630,9 @@ static void ProcessDeclAttribute(Sema &S
   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;

Added: cfe/trunk/test/SemaCXX/attr-require-constant-initialization.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/attr-require-constant-initialization.cpp?rev=280525&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/attr-require-constant-initialization.cpp (added)
+++ cfe/trunk/test/SemaCXX/attr-require-constant-initialization.cpp Fri Sep  2 13:53:31 2016
@@ -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -1 {{required by 'require_constant_initializer' attribute here}}
+ATTR NonLit nl_ctor2{}; // expected-error {{variable does not have a constant initializer}}
+// expected-note at -1 {{required by 'require_constant_initializer' attribute here}}
+ATTR NonLit nl_ctor3 = {}; // expected-error {{variable does not have a constant initializer}}
+// expected-note at -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 at -1 {{required by 'require_constant_initializer' attribute here}}
+ATTR StoresNonLit snl; // expected-error {{variable does not have a constant initializer}}
+// expected-note at -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 at -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 at -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 at -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 at -1 {{required by 'require_constant_initializer' attribute here}}
+ATTR NonLit non_const(nullptr); // expected-error {{variable does not have a constant initializer}}
+// expected-warning at -1 {{declaration requires a global destructor}}
+// expected-note at -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 at -1 {{in call to 'TestCtor(42)'}}
+ATTR constexpr TestCtor<NotC> inval_constexpr2(42); // expected-error {{must be initialized by a constant expression}}
+// expected-note at -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)

Modified: cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp?rev=280525&r1=280524&r2=280525&view=diff
==============================================================================
--- cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp (original)
+++ cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp Fri Sep  2 13:53:31 2016
@@ -2790,8 +2790,10 @@ static std::string GenerateLangOptRequir
   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 += " || ";




More information about the cfe-commits mailing list