Diagnose if a __thread or _Thread_local variable has a non-constant initializer
or non-trivial destructor.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@179491 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 79c81c7..17a7f00 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5418,6 +5418,13 @@
   "in-class initializer for static data member is not a constant expression; "
   "folding it to a constant is a GNU extension">, InGroup<GNU>;
 
+def err_thread_dynamic_init : Error<
+  "initializer for thread-local variable must be a constant expression">;
+def err_thread_nontrivial_dtor : Error<
+  "type of thread-local variable has non-trivial destruction">;
+def note_use_thread_local : Note<
+  "use 'thread_local' to allow this">;
+
 // C++ anonymous unions and GNU anonymous structs/unions
 def ext_anonymous_union : Extension<
   "anonymous unions are a C11 extension">, InGroup<C11>;
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 52d39d3..99702be 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -7627,6 +7627,18 @@
     // C99 6.7.8p4. All file scoped initializers need to be constant.
     if (!getLangOpts().CPlusPlus && !VDecl->isInvalidDecl())
       CheckForConstantInitializer(Init, DclT);
+    else if (VDecl->getTLSKind() == VarDecl::TLS_Static &&
+             !VDecl->isInvalidDecl() && !DclT->isDependentType() &&
+             !Init->isValueDependent() && !VDecl->isConstexpr() &&
+             !Init->isEvaluatable(Context)) {
+      // 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(VDecl->getLocation(), diag::err_thread_dynamic_init);
+      if (getLangOpts().CPlusPlus11)
+        Diag(VDecl->getLocation(), diag::note_use_thread_local);
+    }
   }
 
   // We will represent direct-initialization similarly to copy-initialization:
@@ -7972,6 +7984,16 @@
       Diag(var->getLocation(), diag::warn_missing_variable_declarations) << var;
   }
 
+  if (var->getTLSKind() == VarDecl::TLS_Static &&
+      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);
+  }
+
   // All the following checks are C++ only.
   if (!getLangOpts().CPlusPlus) return;
 
diff --git a/test/Sema/thread-specifier.c b/test/Sema/thread-specifier.c
index 426a297..ccffe73 100644
--- a/test/Sema/thread-specifier.c
+++ b/test/Sema/thread-specifier.c
@@ -3,6 +3,7 @@
 // RUN: %clang_cc1 -triple i686-pc-linux-gnu -fsyntax-only -Wno-private-extern -verify -pedantic %s -DC11 -D__thread=_Thread_local
 // RUN: %clang_cc1 -triple i686-pc-linux-gnu -fsyntax-only -Wno-private-extern -verify -pedantic -x c++ %s -DC11 -D__thread=_Thread_local
 // RUN: %clang_cc1 -triple i686-pc-linux-gnu -fsyntax-only -Wno-private-extern -verify -pedantic -x c++ %s -DCXX11 -D__thread=thread_local -std=c++11
+// RUN: %clang_cc1 -triple i686-pc-linux-gnu -fsyntax-only -Wno-private-extern -verify -pedantic -x c++ %s -DC11 -D__thread=_Thread_local -std=c++11
 
 #ifdef __cplusplus
 // In C++, we define __private_extern__ to extern.
@@ -51,8 +52,8 @@
   __thread auto int t12a; // expected-error-re {{cannot combine with previous '(__thread|_Thread_local)' declaration specifier}}
   auto __thread int t12b; // expected-error {{cannot combine with previous 'auto' declaration specifier}}
 #else
-  __thread auto t12a = 0; // expected-error {{'thread_local' variables must have global storage}}
-  auto __thread t12b = 0; // expected-error {{'thread_local' variables must have global storage}}
+  __thread auto t12a = 0; // expected-error-re {{'(t|_T)hread_local' variables must have global storage}}
+  auto __thread t12b = 0; // expected-error-re {{'(t|_T)hread_local' variables must have global storage}}
 #endif
   __thread register int t13a; // expected-error-re {{cannot combine with previous '(__thread|_Thread_local|thread_local)' declaration specifier}}
   register __thread int t13b; // expected-error {{cannot combine with previous 'register' declaration specifier}}
@@ -83,3 +84,27 @@
 #if __cplusplus >= 201103L
 constexpr int *thread_int_ptr_2 = &thread_int; // expected-error {{must be initialized by a constant expression}}
 #endif
+
+int non_const();
+__thread int non_const_init = non_const();
+#if !defined(__cplusplus)
+// expected-error@-2 {{initializer element is not a compile-time constant}}
+#elif !defined(CXX11)
+// expected-error@-4 {{initializer for thread-local variable must be a constant expression}}
+#if __cplusplus >= 201103L
+// expected-note@-6 {{use 'thread_local' to allow this}}
+#endif
+#endif
+
+#ifdef __cplusplus
+struct S {
+  ~S();
+};
+__thread S s;
+#if !defined(CXX11)
+// expected-error@-2 {{type of thread-local variable has non-trivial destruction}}
+#if __cplusplus >= 201103L
+// expected-note@-4 {{use 'thread_local' to allow this}}
+#endif
+#endif
+#endif