diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 7927279ddd..f0832dc7bf 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -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(VarDeclBits.TLSKind); } /// hasLocalStorage - Returns true if a variable with function scope diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index fe3479e332..79c81c791a 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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< diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index b1d174b855..340cc41f7e 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -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()) diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp index c3bf8f89b2..995925350c 100644 --- a/lib/AST/DeclPrinter.cpp +++ b/lib/AST/DeclPrinter.cpp @@ -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(D)) diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index d7abe3082e..d499228af3 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -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()) { if (const VarDecl *Var = dyn_cast(VD)) { - if (Var->isThreadSpecified()) + if (Var->getTLSKind()) return false; } } diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index 5375c5e18f..8865399b70 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/lib/CodeGen/CGDecl.cpp @@ -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; diff --git a/lib/CodeGen/CGDeclCXX.cpp b/lib/CodeGen/CGDeclCXX.cpp index 0448d31f40..609f6aa7c6 100644 --- a/lib/CodeGen/CGDeclCXX.cpp +++ b/lib/CodeGen/CGDeclCXX.cpp @@ -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. diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index 4d72dc1c88..c515508875 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -1667,7 +1667,7 @@ static void setObjCGCLValueClass(const ASTContext &Ctx, const Expr *E, if (const VarDecl *VD = dyn_cast(Exp->getDecl())) { if (VD->hasGlobalStorage()) { LV.setGlobalObjCRef(true); - LV.setThreadLocalRef(VD->isThreadSpecified()); + LV.setThreadLocalRef(VD->getTLSKind() != VarDecl::TLS_None); } } LV.setObjCArray(E->getType()->isArrayType()); diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index aacabc7867..20532d68c9 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -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()) || D->getAttr()) && !D->hasExternalStorage() && !D->getInit() && - !D->getAttr() && !D->isThreadSpecified() && + !D->getAttr() && !D->getTLSKind() && !D->getAttr()) { // Thread local vars aren't considered common linkage. return llvm::GlobalVariable::CommonLinkage; diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index a2c3c2e147..b3cbbf203e 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -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(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 diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index f832809b2d..1fc1a7cc48 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -284,7 +284,7 @@ static bool mayBeSharedVariable(const Decl *D) { if (isa(D)) return true; if (const VarDecl *vd = dyn_cast(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(D) || !cast(D)->isThreadSpecified()) { + if (!isa(D) || !cast(D)->getTLSKind()) { S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type) << Attr.getName() << ExpectedTLSVar; return; diff --git a/lib/Sema/SemaOpenMP.cpp b/lib/Sema/SemaOpenMP.cpp index b8acb2d731..c815d4f9ab 100644 --- a/lib/Sema/SemaOpenMP.cpp +++ b/lib/Sema/SemaOpenMP.cpp @@ -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; diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 990626189e..ae091e3da3 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -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); diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 17e3218bff..868369e87c 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -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()); diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 0fbdd7e5da..cad6ea77b6 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -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++]; diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 023599d0db..ef9e60f32e 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -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 diff --git a/test/CXX/temp/temp.arg/temp.arg.nontype/p1-11.cpp b/test/CXX/temp/temp.arg/temp.arg.nontype/p1-11.cpp index 59ce8b68b7..2eaa0ba304 100644 --- a/test/CXX/temp/temp.arg/temp.arg.nontype/p1-11.cpp +++ b/test/CXX/temp/temp.arg/temp.arg.nontype/p1-11.cpp @@ -4,16 +4,20 @@ namespace std { typedef decltype(nullptr) nullptr_t; } -template struct IP { // expected-note 4 {{template parameter is declared here}} +template struct IP { // expected-note 5 {{template parameter is declared here}} IP *ip2; }; +template 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 ip2; @@ -23,6 +27,9 @@ IP ip5; IP 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 ir1; // expected-error{{non-type template argument refers to thread-local object}} struct X { }; template struct PM { // expected-note 2 {{template parameter is declared here}} diff --git a/test/Misc/ast-dump-decl.c b/test/Misc/ast-dump-decl.c index c74da29f6d..94335b825c 100644 --- a/test/Misc/ast-dump-decl.c +++ b/test/Misc/ast-dump-decl.c @@ -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__ diff --git a/test/Misc/ast-dump-decl.cpp b/test/Misc/ast-dump-decl.cpp index c8f7d2fe6c..b11a265a70 100644 --- a/test/Misc/ast-dump-decl.cpp +++ b/test/Misc/ast-dump-decl.cpp @@ -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 index 0000000000..4679946e1a --- /dev/null +++ b/test/PCH/thread-local.cpp @@ -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 diff --git a/test/Sema/thread-specifier.c b/test/Sema/thread-specifier.c index ca6c41192b..426a297261 100644 --- a/test/Sema/thread-specifier.c +++ b/test/Sema/thread-specifier.c @@ -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;