]> granicus.if.org Git - clang/commitdiff
Annotate flavor of TLS variable (statically or dynamically initialized) onto the...
authorRichard Smith <richard-llvm@metafoo.co.uk>
Sat, 13 Apr 2013 02:43:54 +0000 (02:43 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Sat, 13 Apr 2013 02:43:54 +0000 (02:43 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@179447 91177308-0d34-0410-b5e6-96231b3b80d8

21 files changed:
include/clang/AST/Decl.h
include/clang/Basic/DiagnosticSemaKinds.td
lib/AST/ASTDumper.cpp
lib/AST/DeclPrinter.cpp
lib/AST/ExprConstant.cpp
lib/CodeGen/CGDecl.cpp
lib/CodeGen/CGDeclCXX.cpp
lib/CodeGen/CGExpr.cpp
lib/CodeGen/CodeGenModule.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclAttr.cpp
lib/Sema/SemaOpenMP.cpp
lib/Sema/SemaTemplate.cpp
lib/Sema/SemaTemplateInstantiateDecl.cpp
lib/Serialization/ASTReaderDecl.cpp
lib/Serialization/ASTWriterDecl.cpp
test/CXX/temp/temp.arg/temp.arg.nontype/p1-11.cpp
test/Misc/ast-dump-decl.c
test/Misc/ast-dump-decl.cpp
test/PCH/thread-local.cpp [new file with mode: 0644]
test/Sema/thread-specifier.c

index 7927279ddd6d4276de665db93b9f780ccca71507..f0832dc7bf3205367d3a90640191ef36250b1b18 100644 (file)
@@ -641,6 +641,13 @@ public:
     ListInit  ///< Direct list-initialization (C++11)
   };
 
+  /// \brief Kinds of thread-local storage.
+  enum TLSKind {
+    TLS_None,   ///< Not a TLS variable.
+    TLS_Static, ///< TLS with a known-constant initializer.
+    TLS_Dynamic ///< TLS with a dynamic initializer.
+  };
+
 protected:
   /// \brief Placeholder type used in Init to denote an unparsed C++ default
   /// argument.
@@ -664,7 +671,7 @@ private:
     friend class ASTDeclReader;
 
     unsigned SClass : 3;
-    unsigned ThreadSpecified : 1;
+    unsigned TLSKind : 2;
     unsigned InitStyle : 2;
 
     /// \brief Whether this variable is the exception variable in a C++ catch
@@ -687,7 +694,7 @@ private:
     /// \brief Whether this variable is (C++0x) constexpr.
     unsigned IsConstexpr : 1;
   };
-  enum { NumVarDeclBits = 14 };
+  enum { NumVarDeclBits = 12 };
 
   friend class ASTDeclReader;
   friend class StmtIteratorBase;
@@ -771,9 +778,9 @@ public:
   }
   void setStorageClass(StorageClass SC);
 
-  void setThreadSpecified(bool T) { VarDeclBits.ThreadSpecified = T; }
-  bool isThreadSpecified() const {
-    return VarDeclBits.ThreadSpecified;
+  void setTLSKind(TLSKind TLS) { VarDeclBits.TLSKind = TLS; }
+  TLSKind getTLSKind() const {
+    return static_cast<TLSKind>(VarDeclBits.TLSKind);
   }
 
   /// hasLocalStorage - Returns true if a variable with function scope
index fe3479e33287db3f1b96e6da3b395a2948fd87c9..79c81c791a31ec690c942d4f864f3a94127b3d93 100644 (file)
@@ -3369,6 +3369,9 @@ def err_non_thread_thread : Error<
   "non-thread-local declaration of %0 follows thread-local declaration">;
 def err_thread_non_thread : Error<
   "thread-local declaration of %0 follows non-thread-local declaration">;
+def err_thread_thread_different_kind : Error<
+  "thread-local declaration of %0 with %select{static|dynamic}1 initialization "
+  "follows declaration with %select{dynamic|static}1 initialization">;
 def err_redefinition_different_type : Error<
   "redefinition of %0 with a different type%diff{: $ vs $|}1,2">;
 def err_redefinition_different_kind : Error<
index b1d174b855eb9f8e05f761b7a5634498a49f3756..340cc41f7e81f31487f51a5fac08bd08dd983322 100644 (file)
@@ -853,8 +853,11 @@ void ASTDumper::VisitVarDecl(const VarDecl *D) {
   StorageClass SC = D->getStorageClass();
   if (SC != SC_None)
     OS << ' ' << VarDecl::getStorageClassSpecifierString(SC);
-  if (D->isThreadSpecified())
-    OS << " __thread";
+  switch (D->getTLSKind()) {
+  case VarDecl::TLS_None: break;
+  case VarDecl::TLS_Static: OS << " tls"; break;
+  case VarDecl::TLS_Dynamic: OS << " tls_dynamic"; break;
+  }
   if (D->isModulePrivate())
     OS << " __module_private__";
   if (D->isNRVOVariable())
index c3bf8f89b2976a1e740fd2ad7a8fd700e7b422b6..995925350c3e39c99c28fdd027e9572c792fd734 100644 (file)
@@ -641,14 +641,25 @@ void DeclPrinter::VisitLabelDecl(LabelDecl *D) {
 
 
 void DeclPrinter::VisitVarDecl(VarDecl *D) {
-  StorageClass SC = D->getStorageClass();
-  if (!Policy.SuppressSpecifiers && SC != SC_None)
-    Out << VarDecl::getStorageClassSpecifierString(SC) << " ";
+  if (!Policy.SuppressSpecifiers) {
+    StorageClass SC = D->getStorageClass();
+    if (SC != SC_None)
+      Out << VarDecl::getStorageClassSpecifierString(SC) << " ";
 
-  if (!Policy.SuppressSpecifiers && D->isThreadSpecified())
-    Out << "__thread ";
-  if (!Policy.SuppressSpecifiers && D->isModulePrivate())
-    Out << "__module_private__ ";
+    switch (D->getTLSKind()) {
+    case VarDecl::TLS_None:
+      break;
+    case VarDecl::TLS_Static:
+      Out << "_Thread_local ";
+      break;
+    case VarDecl::TLS_Dynamic:
+      Out << "thread_local ";
+      break;
+    }
+
+    if (D->isModulePrivate())
+      Out << "__module_private__ ";
+  }
 
   QualType T = D->getType();
   if (ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D))
index d7abe3082e58052b77d06655cc17ef666cd6759c..d499228af3228d4657a359f7bf1bae1b1346caf2 100644 (file)
@@ -999,7 +999,7 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
   // Check if this is a thread-local variable.
   if (const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>()) {
     if (const VarDecl *Var = dyn_cast<const VarDecl>(VD)) {
-      if (Var->isThreadSpecified())
+      if (Var->getTLSKind())
         return false;
     }
   }
index 5375c5e18f2f8a3484de99cdf5bd6fc90dcd81ae..8865399b70ecba0b089dac36ba0e0f18b443dfb2 100644 (file)
@@ -198,7 +198,7 @@ CodeGenFunction::CreateStaticVarDecl(const VarDecl &D,
   if (Linkage != llvm::GlobalValue::InternalLinkage)
     GV->setVisibility(CurFn->getVisibility());
 
-  if (D.isThreadSpecified())
+  if (D.getTLSKind())
     CGM.setTLSMode(GV, D);
 
   return GV;
index 0448d31f4073d9d7da025113f8167aba5616b1b5..609f6aa7c6e6ee92b75b1bca63c6453185aaec70 100644 (file)
@@ -39,7 +39,7 @@ static void EmitDeclInit(CodeGenFunction &CGF, const VarDecl &D,
     CodeGenModule &CGM = CGF.CGM;
     if (lv.isObjCStrong())
       CGM.getObjCRuntime().EmitObjCGlobalAssign(CGF, CGF.EmitScalarExpr(Init),
-                                                DeclPtr, D.isThreadSpecified());
+                                                DeclPtr, D.getTLSKind());
     else if (lv.isObjCWeak())
       CGM.getObjCRuntime().EmitObjCWeakAssign(CGF, CGF.EmitScalarExpr(Init),
                                               DeclPtr);
@@ -218,6 +218,9 @@ void CodeGenFunction::EmitCXXGuardedInit(const VarDecl &D,
               "this initialization requires a guard variable, which "
               "the kernel does not support");
 
+  if (D.getTLSKind())
+    CGM.ErrorUnsupported(D.getInit(), "dynamic TLS initialization");
+
   CGM.getCXXABI().EmitGuardedInit(*this, D, DeclPtr, PerformInit);
 }
 
@@ -254,6 +257,9 @@ void
 CodeGenModule::EmitCXXGlobalVarDeclInitFunc(const VarDecl *D,
                                             llvm::GlobalVariable *Addr,
                                             bool PerformInit) {
+  if (D->getTLSKind())
+    ErrorUnsupported(D->getInit(), "dynamic TLS initialization");
+
   llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false);
 
   // Create a variable initialization function.
index 4d72dc1c884e1f7de7e4f0f9aaeabec6fc06ad44..c515508875836835226687145b90bcb246bb9f98 100644 (file)
@@ -1667,7 +1667,7 @@ static void setObjCGCLValueClass(const ASTContext &Ctx, const Expr *E,
     if (const VarDecl *VD = dyn_cast<VarDecl>(Exp->getDecl())) {
       if (VD->hasGlobalStorage()) {
         LV.setGlobalObjCRef(true);
-        LV.setThreadLocalRef(VD->isThreadSpecified());
+        LV.setThreadLocalRef(VD->getTLSKind() != VarDecl::TLS_None);
       }
     }
     LV.setObjCArray(E->getType()->isArrayType());
index aacabc7867902d8be7d90513416eb6be8e8b7f84..20532d68c9eb24f22d04e9c7e855e7e82417aaca 100644 (file)
@@ -333,7 +333,7 @@ static llvm::GlobalVariable::ThreadLocalMode GetLLVMTLSModel(
 
 void CodeGenModule::setTLSMode(llvm::GlobalVariable *GV,
                                const VarDecl &D) const {
-  assert(D.isThreadSpecified() && "setting TLS mode on non-TLS var!");
+  assert(D.getTLSKind() && "setting TLS mode on non-TLS var!");
 
   llvm::GlobalVariable::ThreadLocalMode TLM;
   TLM = GetLLVMTLSModel(CodeGenOpts.getDefaultTLSModel());
@@ -1485,7 +1485,7 @@ CodeGenModule::GetOrCreateLLVMGlobal(StringRef MangledName,
         GV->setVisibility(GetLLVMVisibility(LV.getVisibility()));
     }
 
-    if (D->isThreadSpecified())
+    if (D->getTLSKind())
       setTLSMode(GV, *D);
   }
 
@@ -1915,7 +1915,7 @@ CodeGenModule::GetLLVMLinkageVarDefinition(const VarDecl *D,
            ((!CodeGenOpts.NoCommon && !D->getAttr<NoCommonAttr>()) ||
              D->getAttr<CommonAttr>()) &&
            !D->hasExternalStorage() && !D->getInit() &&
-           !D->getAttr<SectionAttr>() && !D->isThreadSpecified() &&
+           !D->getAttr<SectionAttr>() && !D->getTLSKind() &&
            !D->getAttr<WeakImportAttr>()) {
     // Thread local vars aren't considered common linkage.
     return llvm::GlobalVariable::CommonLinkage;
index a2c3c2e14723803d18acbf49613932f83e8ecf1c..b3cbbf203e284463d576a2193bac426433ae6c8b 100644 (file)
@@ -2953,12 +2953,22 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous,
     return New->setInvalidDecl();
   }
 
-  if (New->isThreadSpecified() && !Old->isThreadSpecified()) {
-    Diag(New->getLocation(), diag::err_thread_non_thread) << New->getDeclName();
-    Diag(Old->getLocation(), diag::note_previous_definition);
-  } else if (!New->isThreadSpecified() && Old->isThreadSpecified()) {
-    Diag(New->getLocation(), diag::err_non_thread_thread) << New->getDeclName();
-    Diag(Old->getLocation(), diag::note_previous_definition);
+  if (New->getTLSKind() != Old->getTLSKind()) {
+    if (!Old->getTLSKind()) {
+      Diag(New->getLocation(), diag::err_thread_non_thread) << New->getDeclName();
+      Diag(Old->getLocation(), diag::note_previous_declaration);
+    } else if (!New->getTLSKind()) {
+      Diag(New->getLocation(), diag::err_non_thread_thread) << New->getDeclName();
+      Diag(Old->getLocation(), diag::note_previous_declaration);
+    } else {
+      // Do not allow redeclaration to change the variable between requiring
+      // static and dynamic initialization.
+      // FIXME: GCC allows this, but uses the TLS keyword on the first
+      // declaration to determine the kind. Do we need to be compatible here?
+      Diag(New->getLocation(), diag::err_thread_thread_different_kind)
+        << New->getDeclName() << (New->getTLSKind() == VarDecl::TLS_Dynamic);
+      Diag(Old->getLocation(), diag::note_previous_declaration);
+    }
   }
 
   // C++ doesn't have tentative definitions, so go right ahead and check here.
@@ -4577,7 +4587,7 @@ bool Sema::inferObjCARCLifetime(ValueDecl *decl) {
   if (VarDecl *var = dyn_cast<VarDecl>(decl)) {
     // Thread-local variables cannot have lifetime.
     if (lifetime && lifetime != Qualifiers::OCL_ExplicitNone &&
-        var->isThreadSpecified()) {
+        var->getTLSKind()) {
       Diag(var->getLocation(), diag::err_arc_thread_ownership)
         << var->getType();
       return true;
@@ -4851,9 +4861,9 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC,
       Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(),
            diag::err_thread_unsupported);
     else
-      // FIXME: Track which thread specifier was used; they have different
-      // semantics.
-      NewVD->setThreadSpecified(true);
+      NewVD->setTLSKind(TSCS == DeclSpec::TSCS_thread_local
+                          ? VarDecl::TLS_Dynamic
+                          : VarDecl::TLS_Static);
   }
 
   // C99 6.7.4p3
index f832809b2df6faa37c750c5ace453c714ca8a3f7..1fc1a7cc4888461d2541a8361aa00594c6695286 100644 (file)
@@ -284,7 +284,7 @@ static bool mayBeSharedVariable(const Decl *D) {
   if (isa<FieldDecl>(D))
     return true;
   if (const VarDecl *vd = dyn_cast<VarDecl>(D))
-    return (vd->hasGlobalStorage() && !(vd->isThreadSpecified()));
+    return vd->hasGlobalStorage() && !vd->getTLSKind();
 
   return false;
 }
@@ -1656,7 +1656,7 @@ static void handleTLSModelAttr(Sema &S, Decl *D,
     return;
   }
 
-  if (!isa<VarDecl>(D) || !cast<VarDecl>(D)->isThreadSpecified()) {
+  if (!isa<VarDecl>(D) || !cast<VarDecl>(D)->getTLSKind()) {
     S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type)
       << Attr.getName() << ExpectedTLSVar;
     return;
index b8acb2d7310eb49c905e2fde6c807affeb7902d2..c815d4f9abc79ebe4a8d613902e7202a15b5238b 100644 (file)
@@ -165,8 +165,8 @@ OMPThreadPrivateDecl *Sema::CheckOMPThreadPrivateDecl(
       continue;
     }
 
-    // Check if threadspecified is set.
-    if (VD->isThreadSpecified()) {
+    // Check if this is a TLS variable.
+    if (VD->getTLSKind()) {
       Diag(ILoc, diag::err_omp_var_thread_local) << VD;
       Diag(VD->getLocation(), diag::note_forward_declaration) << VD;
       continue;
index 990626189e18b1d714da18cdf80f8bd0c4905ea6..ae091e3da341c997f186e4c2233291e84655b357 100644 (file)
@@ -3842,8 +3842,7 @@ CheckTemplateArgumentAddressOfObjectOrFunction(Sema &S,
     }
 
     // A template argument must have static storage duration.
-    // FIXME: Ensure this works for thread_local as well as __thread.
-    if (Var->isThreadSpecified()) {
+    if (Var->getTLSKind()) {
       S.Diag(Arg->getLocStart(), diag::err_template_arg_thread_local)
         << Arg->getSourceRange();
       S.Diag(Var->getLocation(), diag::note_template_arg_refers_here);
index 17e3218bffc1338cfa8a231df8dcf13d021a2b3b..868369e87cdf7ec262bfcfcd75a149f8bc570464 100644 (file)
@@ -339,7 +339,7 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) {
                                  D->getLocation(), D->getIdentifier(),
                                  DI->getType(), DI,
                                  D->getStorageClass());
-  Var->setThreadSpecified(D->isThreadSpecified());
+  Var->setTLSKind(D->getTLSKind());
   Var->setInitStyle(D->getInitStyle());
   Var->setCXXForRangeDecl(D->isCXXForRangeDecl());
   Var->setConstexpr(D->isConstexpr());
index 0fbdd7e5daeb852771bc56365eaf4aa1d01594fd..cad6ea77b675c177244d0a0c66014a1dc8c4816a 100644 (file)
@@ -895,7 +895,7 @@ void ASTDeclReader::VisitVarDecl(VarDecl *VD) {
   VisitDeclaratorDecl(VD);
 
   VD->VarDeclBits.SClass = (StorageClass)Record[Idx++];
-  VD->VarDeclBits.ThreadSpecified = Record[Idx++];
+  VD->VarDeclBits.TLSKind = Record[Idx++];
   VD->VarDeclBits.InitStyle = Record[Idx++];
   VD->VarDeclBits.ExceptionVar = Record[Idx++];
   VD->VarDeclBits.NRVOVariable = Record[Idx++];
index 023599d0db83bf0719b4d925137687dd3459fbbf..ef9e60f32e8cf1ee266f13cc596f216e78599815 100644 (file)
@@ -677,7 +677,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) {
   VisitRedeclarable(D);
   VisitDeclaratorDecl(D);
   Record.push_back(D->getStorageClass());
-  Record.push_back(D->isThreadSpecified());
+  Record.push_back(D->getTLSKind());
   Record.push_back(D->getInitStyle());
   Record.push_back(D->isExceptionVariable());
   Record.push_back(D->isNRVOVariable());
@@ -766,7 +766,7 @@ void ASTDeclWriter::VisitParmVarDecl(ParmVarDecl *D) {
 
   // Check things we know are true of *every* PARM_VAR_DECL, which is more than
   // just us assuming it.
-  assert(!D->isThreadSpecified() && "PARM_VAR_DECL can't be __thread");
+  assert(!D->getTLSKind() && "PARM_VAR_DECL can't use TLS");
   assert(D->getAccess() == AS_none && "PARM_VAR_DECL can't be public/private");
   assert(!D->isExceptionVariable() && "PARM_VAR_DECL can't be exception var");
   assert(D->getPreviousDecl() == 0 && "PARM_VAR_DECL can't be redecl");
@@ -1515,7 +1515,7 @@ void ASTWriter::WriteDeclsBlockAbbrevs() {
   Abv->Add(BitCodeAbbrevOp(0));                       // hasExtInfo
   // VarDecl
   Abv->Add(BitCodeAbbrevOp(0));                       // StorageClass
-  Abv->Add(BitCodeAbbrevOp(0));                       // isThreadSpecified
+  Abv->Add(BitCodeAbbrevOp(0));                       // getTLSKind
   Abv->Add(BitCodeAbbrevOp(0));                       // hasCXXDirectInitializer
   Abv->Add(BitCodeAbbrevOp(0));                       // isExceptionVariable
   Abv->Add(BitCodeAbbrevOp(0));                       // isNRVOVariable
@@ -1594,7 +1594,7 @@ void ASTWriter::WriteDeclsBlockAbbrevs() {
   Abv->Add(BitCodeAbbrevOp(0));                       // hasExtInfo
   // VarDecl
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // StorageClass
-  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isThreadSpecified
+  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // getTLSKind
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // CXXDirectInitializer
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isExceptionVariable
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isNRVOVariable
index 59ce8b68b7c8ae49cf0a0102ccdd1609b098cccd..2eaa0ba304f4fa0d90ff98f373a17e5f2916ec2b 100644 (file)
@@ -4,16 +4,20 @@ namespace std {
   typedef decltype(nullptr) nullptr_t;
 }
 
-template<int *ip> struct IP {  // expected-note 4 {{template parameter is declared here}}
+template<int *ip> struct IP {  // expected-note 5 {{template parameter is declared here}}
   IP<ip> *ip2;
 };
 
+template<int &ip> struct IR {};
+
 constexpr std::nullptr_t get_nullptr() { return nullptr; }
 
 constexpr std::nullptr_t np = nullptr;
 
 std::nullptr_t nonconst_np; // expected-note{{declared here}}
 
+thread_local int tl; // expected-note {{refers here}}
+
 IP<0> ip0; // expected-error{{null non-type template argument must be cast to template parameter type 'int *'}}
 IP<(0)> ip1; // expected-error{{null non-type template argument must be cast to template parameter type 'int *'}}
 IP<nullptr> ip2;
@@ -23,6 +27,9 @@ IP<np> ip5;
 IP<nonconst_np> ip5; // expected-error{{non-type template argument of type 'std::nullptr_t' (aka 'nullptr_t') is not a constant expression}} \
 // expected-note{{read of non-constexpr variable 'nonconst_np' is not allowed in a constant expression}}
 IP<(float*)0> ip6; // expected-error{{null non-type template argument of type 'float *' does not match template parameter of type 'int *'}}
+IP<&tl> ip7; // expected-error{{non-type template argument of type 'int *' is not a constant expression}}
+
+IR<tl> ir1; // expected-error{{non-type template argument refers to thread-local object}}
 
 struct X { };
 template<int X::*pm> struct PM { // expected-note 2 {{template parameter is declared here}}
index c74da29f6d6901088e3998d1b6d88876562be98e..94335b825c86bd939209eec46932375fe2d77edf 100644 (file)
@@ -139,7 +139,7 @@ extern int TestVarDeclSC;
 // CHECK:      VarDecl{{.*}} TestVarDeclSC 'int' extern
 
 __thread int TestVarDeclThread;
-// CHECK:      VarDecl{{.*}} TestVarDeclThread 'int' __thread
+// CHECK:      VarDecl{{.*}} TestVarDeclThread 'int' tls{{$}}
 
 __module_private__ int TestVarDeclPrivate;
 // CHECK:      VarDecl{{.*}} TestVarDeclPrivate 'int' __module_private__
index c8f7d2fe6ccf53dee3e701e782b5f287cccf1072..b11a265a707eddc44947ab417bbd3c25f98d9bc3 100644 (file)
@@ -92,6 +92,9 @@ class TestCXXRecordDeclPack : public T... {
 // CHECK-NEXT:   public 'T'...
 // CHECK-NEXT:   CXXRecordDecl{{.*}} class TestCXXRecordDeclPack
 
+thread_local int TestThreadLocalInt;
+// CHECK: TestThreadLocalInt {{.*}} tls_dynamic
+
 __module_private__ class TestCXXRecordDeclPrivate;
 // CHECK: CXXRecordDecl{{.*}} class TestCXXRecordDeclPrivate __module_private__
 
diff --git a/test/PCH/thread-local.cpp b/test/PCH/thread-local.cpp
new file mode 100644 (file)
index 0000000..4679946
--- /dev/null
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -pedantic-errors -std=c++11 -emit-pch %s -o %t
+// RUN: %clang_cc1 -pedantic-errors -std=c++11 -include-pch %t -verify %s
+
+#ifndef HEADER_INCLUDED
+
+#define HEADER_INCLUDED
+extern thread_local int a;
+extern _Thread_local int b;
+extern int c;
+
+#else
+
+_Thread_local int a; // expected-error {{thread-local declaration of 'a' with static initialization follows declaration with dynamic initialization}}
+// expected-note@7 {{previous declaration is here}}
+thread_local int b; // expected-error {{thread-local declaration of 'b' with dynamic initialization follows declaration with static initialization}}
+// expected-note@8 {{previous declaration is here}}
+thread_local int c; // expected-error {{thread-local declaration of 'c' follows non-thread-local declaration}}
+// expected-note@9 {{previous declaration is here}}
+
+#endif
index ca6c41192b2469b8fdd22e80cbb687aebb405bab..426a297261986f751ec4fade9626fd0b96c6bf22 100644 (file)
@@ -59,11 +59,18 @@ int f(__thread int t7) { // expected-error {{' is only allowed on variable decla
 }
 
 __thread typedef int t14; // expected-error-re {{cannot combine with previous '(__thread|_Thread_local|thread_local)' declaration specifier}}
-__thread int t15; // expected-note {{previous definition is here}}
+__thread int t15; // expected-note {{previous declaration is here}}
 extern int t15; // expected-error {{non-thread-local declaration of 't15' follows thread-local declaration}}
-extern int t16; // expected-note {{previous definition is here}}
+extern int t16; // expected-note {{previous declaration is here}}
 __thread int t16; // expected-error {{thread-local declaration of 't16' follows non-thread-local declaration}}
 
+#ifdef CXX11
+extern thread_local int t17; // expected-note {{previous declaration is here}}
+_Thread_local int t17; // expected-error {{thread-local declaration of 't17' with static initialization follows declaration with dynamic initialization}}
+extern _Thread_local int t18; // expected-note {{previous declaration is here}}
+thread_local int t18; // expected-error {{thread-local declaration of 't18' with dynamic initialization follows declaration with static initialization}}
+#endif
+
 // PR13720
 __thread int thread_int;
 int *thread_int_ptr = &thread_int;