From ec64244f5939fa81596fbeddad966cca4b4a4c51 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 12 Apr 2013 22:46:28 +0000 Subject: [PATCH] Parsing support for thread_local and _Thread_local. We give them the same semantics as __thread for now. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@179424 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 7 +- include/clang/Basic/TokenKinds.def | 1 + include/clang/Sema/DeclSpec.h | 40 ++++++++--- lib/Parse/ParseDecl.cpp | 22 ++++-- lib/Parse/ParseTentative.cpp | 13 ++-- lib/Parse/Parser.cpp | 4 +- lib/Sema/DeclSpec.cpp | 80 +++++++++++++++++----- lib/Sema/SemaCodeComplete.cpp | 7 +- lib/Sema/SemaDecl.cpp | 41 ++++++----- lib/Sema/SemaDeclCXX.cpp | 38 +++++----- lib/Sema/SemaDeclObjC.cpp | 12 ++-- test/CXX/class/class.friend/p6.cpp | 12 +++- test/Parser/cxx0x-decl.cpp | 3 +- test/Sema/thread-specifier.c | 74 ++++++++++++++++---- 14 files changed, 249 insertions(+), 105 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index d93de39f14..fe3479e332 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -296,9 +296,9 @@ def warn_exit_time_destructor : Warning< InGroup, DefaultIgnore; def err_invalid_thread : Error< - "'__thread' is only allowed on variable declarations">; + "'%0' is only allowed on variable declarations">; def err_thread_non_global : Error< - "'__thread' variables must have global storage">; + "'%0' variables must have global storage">; def err_thread_unsupported : Error< "thread-local storage is unsupported for the current target">; @@ -4473,8 +4473,7 @@ def err_catch_param_not_objc_type : Error< def err_illegal_qualifiers_on_catch_parm : Error< "illegal qualifiers on @catch parameter">; def err_storage_spec_on_catch_parm : Error< - "@catch parameter cannot have storage specifier %select{|'typedef'|'extern'|" - "'static'|'auto'|'register'|'__private_extern__'|'mutable'}0">; + "@catch parameter cannot have storage specifier '%0'">; def warn_register_objc_catch_parm : Warning< "'register' storage specifier on @catch parameter will be ignored">; def err_qualified_objc_catch_parm : Error< diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def index a254faef76..fb2edcec0d 100644 --- a/include/clang/Basic/TokenKinds.def +++ b/include/clang/Basic/TokenKinds.def @@ -262,6 +262,7 @@ KEYWORD(_Generic , KEYALL) KEYWORD(_Imaginary , KEYALL) KEYWORD(_Noreturn , KEYALL) KEYWORD(_Static_assert , KEYALL) +KEYWORD(_Thread_local , KEYALL) KEYWORD(__func__ , KEYALL) KEYWORD(__objc_yes , KEYALL) KEYWORD(__objc_no , KEYALL) diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index 5b90784dcf..90f513f271 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -226,6 +226,19 @@ public: SCS_private_extern, SCS_mutable }; + /// \brief Thread storage-class-specifier. These can be combined with + /// SCS_extern and SCS_static. + enum TSCS { + TSCS_unspecified, + /// GNU __thread. + TSCS___thread, + /// C++11 thread_local. Implies 'static' at block scope, but not at + /// class scope. + TSCS_thread_local, + /// C11 _Thread_local. Must be combined with either 'static' or 'extern' + /// if used at block scope. + TSCS__Thread_local + }; // Import type specifier width enumeration and constants. typedef TypeSpecifierWidth TSW; @@ -310,7 +323,7 @@ public: private: // storage-class-specifier /*SCS*/unsigned StorageClassSpec : 3; - unsigned SCS_thread_specified : 1; + /*TSCS*/unsigned ThreadStorageClassSpec : 2; unsigned SCS_extern_in_linkage_spec : 1; // type-specifier @@ -362,7 +375,7 @@ private: // the setting was synthesized. SourceRange Range; - SourceLocation StorageClassSpecLoc, SCS_threadLoc; + SourceLocation StorageClassSpecLoc, ThreadStorageClassSpecLoc; SourceLocation TSWLoc, TSCLoc, TSSLoc, TSTLoc, AltiVecLoc; /// TSTNameLoc - If TypeSpecType is any of class, enum, struct, union, /// typename, then this is the location of the named type (if present); @@ -398,7 +411,7 @@ public: DeclSpec(AttributeFactory &attrFactory) : StorageClassSpec(SCS_unspecified), - SCS_thread_specified(false), + ThreadStorageClassSpec(TSCS_unspecified), SCS_extern_in_linkage_spec(false), TypeSpecWidth(TSW_unspecified), TypeSpecComplex(TSC_unspecified), @@ -428,21 +441,25 @@ public: } // storage-class-specifier SCS getStorageClassSpec() const { return (SCS)StorageClassSpec; } - bool isThreadSpecified() const { return SCS_thread_specified; } + TSCS getThreadStorageClassSpec() const { + return (TSCS)ThreadStorageClassSpec; + } bool isExternInLinkageSpec() const { return SCS_extern_in_linkage_spec; } void setExternInLinkageSpec(bool Value) { SCS_extern_in_linkage_spec = Value; } SourceLocation getStorageClassSpecLoc() const { return StorageClassSpecLoc; } - SourceLocation getThreadSpecLoc() const { return SCS_threadLoc; } + SourceLocation getThreadStorageClassSpecLoc() const { + return ThreadStorageClassSpecLoc; + } void ClearStorageClassSpecs() { - StorageClassSpec = DeclSpec::SCS_unspecified; - SCS_thread_specified = false; + StorageClassSpec = DeclSpec::SCS_unspecified; + ThreadStorageClassSpec = DeclSpec::TSCS_unspecified; SCS_extern_in_linkage_spec = false; - StorageClassSpecLoc = SourceLocation(); - SCS_threadLoc = SourceLocation(); + StorageClassSpecLoc = SourceLocation(); + ThreadStorageClassSpecLoc = SourceLocation(); } // type-specifier @@ -494,6 +511,7 @@ public: static const char *getSpecifierName(DeclSpec::TSC C); static const char *getSpecifierName(DeclSpec::TSW W); static const char *getSpecifierName(DeclSpec::SCS S); + static const char *getSpecifierName(DeclSpec::TSCS S); // type-qualifiers @@ -570,8 +588,8 @@ public: /// diagnostics to be ignored when desired. bool SetStorageClassSpec(Sema &S, SCS SC, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID); - bool SetStorageClassSpecThread(SourceLocation Loc, const char *&PrevSpec, - unsigned &DiagID); + bool SetStorageClassSpecThread(TSCS TSC, SourceLocation Loc, + const char *&PrevSpec, unsigned &DiagID); bool SetTypeSpecWidth(TSW W, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID); bool SetTypeSpecComplex(TSC C, SourceLocation Loc, const char *&PrevSpec, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 1979bb12cd..55e2b38271 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1848,7 +1848,8 @@ void Parser::ParseSpecifierQualifierList(DeclSpec &DS, AccessSpecifier AS, if (DS.getStorageClassSpecLoc().isValid()) Diag(DS.getStorageClassSpecLoc(),diag::err_typename_invalid_storageclass); else - Diag(DS.getThreadSpecLoc(), diag::err_typename_invalid_storageclass); + Diag(DS.getThreadStorageClassSpecLoc(), + diag::err_typename_invalid_storageclass); DS.ClearStorageClassSpecs(); } @@ -2181,6 +2182,8 @@ void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs, /// 'auto' /// 'register' /// [C++] 'mutable' +/// [C++11] 'thread_local' +/// [C11] '_Thread_local' /// [GNU] '__thread' /// function-specifier: [C99 6.7.4] /// [C99] 'inline' @@ -2617,7 +2620,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, PrevSpec, DiagID); break; case tok::kw_extern: - if (DS.isThreadSpecified()) + if (DS.getThreadStorageClassSpec() == DeclSpec::TSCS___thread) Diag(Tok, diag::ext_thread_before) << "extern"; isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_extern, Loc, PrevSpec, DiagID); @@ -2627,7 +2630,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, Loc, PrevSpec, DiagID); break; case tok::kw_static: - if (DS.isThreadSpecified()) + if (DS.getThreadStorageClassSpec() == DeclSpec::TSCS___thread) Diag(Tok, diag::ext_thread_before) << "static"; isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_static, Loc, PrevSpec, DiagID); @@ -2656,7 +2659,16 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, PrevSpec, DiagID); break; case tok::kw___thread: - isInvalid = DS.SetStorageClassSpecThread(Loc, PrevSpec, DiagID); + isInvalid = DS.SetStorageClassSpecThread(DeclSpec::TSCS___thread, Loc, + PrevSpec, DiagID); + break; + case tok::kw_thread_local: + isInvalid = DS.SetStorageClassSpecThread(DeclSpec::TSCS_thread_local, Loc, + PrevSpec, DiagID); + break; + case tok::kw__Thread_local: + isInvalid = DS.SetStorageClassSpecThread(DeclSpec::TSCS__Thread_local, + Loc, PrevSpec, DiagID); break; // function-specifier @@ -3903,6 +3915,8 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { case tok::kw_auto: case tok::kw_register: case tok::kw___thread: + case tok::kw_thread_local: + case tok::kw__Thread_local: // Modules case tok::kw___module_private__: diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp index 5e0ef2b83f..00ca3d8465 100644 --- a/lib/Parse/ParseTentative.cpp +++ b/lib/Parse/ParseTentative.cpp @@ -837,11 +837,12 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) { case tok::kw_char16_t: case tok::kw_char32_t: case tok::kw___underlying_type: - case tok::kw_thread_local: case tok::kw__Decimal32: case tok::kw__Decimal64: case tok::kw__Decimal128: case tok::kw___thread: + case tok::kw_thread_local: + case tok::kw__Thread_local: case tok::kw_typeof: case tok::kw___cdecl: case tok::kw___stdcall: @@ -893,7 +894,7 @@ bool Parser::isTentativelyDeclared(IdentifierInfo *II) { /// function-specifier /// 'friend' /// 'typedef' -/// [C++0x] 'constexpr' +/// [C++11] 'constexpr' /// [GNU] attributes declaration-specifiers[opt] /// /// storage-class-specifier: @@ -903,6 +904,8 @@ bool Parser::isTentativelyDeclared(IdentifierInfo *II) { /// 'mutable' /// 'auto' /// [GNU] '__thread' +/// [C++11] 'thread_local' +/// [C11] '_Thread_local' /// /// function-specifier: /// 'inline' @@ -937,8 +940,8 @@ bool Parser::isTentativelyDeclared(IdentifierInfo *II) { /// 'void' /// [GNU] typeof-specifier /// [GNU] '_Complex' -/// [C++0x] 'auto' [TODO] -/// [C++0x] 'decltype' ( expression ) +/// [C++11] 'auto' +/// [C++11] 'decltype' ( expression ) /// /// type-name: /// class-name @@ -1073,6 +1076,8 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, case tok::kw_mutable: case tok::kw_auto: case tok::kw___thread: + case tok::kw_thread_local: + case tok::kw__Thread_local: // function-specifier case tok::kw_inline: case tok::kw_virtual: diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 1ebba3e67a..d819644cb8 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -1141,8 +1141,8 @@ void Parser::ParseKNRParamDeclarations(Declarator &D) { diag::err_invalid_storage_class_in_func_decl); DS.ClearStorageClassSpecs(); } - if (DS.isThreadSpecified()) { - Diag(DS.getThreadSpecLoc(), + if (DS.getThreadStorageClassSpec() != DeclSpec::TSCS_unspecified) { + Diag(DS.getThreadStorageClassSpecLoc(), diag::err_invalid_storage_class_in_func_decl); DS.ClearStorageClassSpecs(); } diff --git a/lib/Sema/DeclSpec.cpp b/lib/Sema/DeclSpec.cpp index e1d55dbddc..7a69eef2bb 100644 --- a/lib/Sema/DeclSpec.cpp +++ b/lib/Sema/DeclSpec.cpp @@ -24,6 +24,7 @@ #include "clang/Sema/Sema.h" #include "clang/Sema/SemaDiagnostic.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" #include "llvm/Support/ErrorHandling.h" #include using namespace clang; @@ -325,7 +326,7 @@ bool Declarator::isDeclarationOfFunction() const { unsigned DeclSpec::getParsedSpecifiers() const { unsigned Res = 0; if (StorageClassSpec != SCS_unspecified || - SCS_thread_specified) + ThreadStorageClassSpec != TSCS_unspecified) Res |= PQ_StorageClassSpecifier; if (TypeQualifiers != TQ_unspecified) @@ -367,6 +368,16 @@ const char *DeclSpec::getSpecifierName(DeclSpec::SCS S) { llvm_unreachable("Unknown typespec!"); } +const char *DeclSpec::getSpecifierName(DeclSpec::TSCS S) { + switch (S) { + case DeclSpec::TSCS_unspecified: return "unspecified"; + case DeclSpec::TSCS___thread: return "__thread"; + case DeclSpec::TSCS_thread_local: return "thread_local"; + case DeclSpec::TSCS__Thread_local: return "_Thread_local"; + } + llvm_unreachable("Unknown typespec!"); +} + const char *DeclSpec::getSpecifierName(TSW W) { switch (W) { case TSW_unspecified: return "unspecified"; @@ -510,16 +521,14 @@ bool DeclSpec::SetStorageClassSpec(Sema &S, SCS SC, SourceLocation Loc, return false; } -bool DeclSpec::SetStorageClassSpecThread(SourceLocation Loc, +bool DeclSpec::SetStorageClassSpecThread(TSCS TSC, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID) { - if (SCS_thread_specified) { - PrevSpec = "__thread"; - DiagID = diag::ext_duplicate_declspec; - return true; - } - SCS_thread_specified = true; - SCS_threadLoc = Loc; + if (ThreadStorageClassSpec != TSCS_unspecified) + return BadSpecifier(TSC, (TSCS)ThreadStorageClassSpec, PrevSpec, DiagID); + + ThreadStorageClassSpec = TSC; + ThreadStorageClassSpecLoc = Loc; return false; } @@ -923,6 +932,34 @@ void DeclSpec::Finish(DiagnosticsEngine &D, Preprocessor &PP) { } } + // C11 6.7.1/3, C++11 [dcl.stc]p1, GNU TLS: __thread, thread_local and + // _Thread_local can only appear with the 'static' and 'extern' storage class + // specifiers. We also allow __private_extern__ as an extension. + if (ThreadStorageClassSpec != TSCS_unspecified) { + switch (StorageClassSpec) { + case SCS_unspecified: + case SCS_extern: + case SCS_private_extern: + case SCS_static: + break; + default: + if (PP.getSourceManager().isBeforeInTranslationUnit( + getThreadStorageClassSpecLoc(), getStorageClassSpecLoc())) + Diag(D, getStorageClassSpecLoc(), + diag::err_invalid_decl_spec_combination) + << DeclSpec::getSpecifierName(getThreadStorageClassSpec()) + << SourceRange(getThreadStorageClassSpecLoc()); + else + Diag(D, getThreadStorageClassSpecLoc(), + diag::err_invalid_decl_spec_combination) + << DeclSpec::getSpecifierName(getStorageClassSpec()) + << SourceRange(getStorageClassSpecLoc()); + // Discard the thread storage class specifier to recover. + ThreadStorageClassSpec = TSCS_unspecified; + ThreadStorageClassSpecLoc = SourceLocation(); + } + } + // If no type specifier was provided and we're parsing a language where // the type specifier is not optional, but we got 'auto' as a storage // class specifier, then assume this is an attempt to use C++0x's 'auto' @@ -952,16 +989,27 @@ void DeclSpec::Finish(DiagnosticsEngine &D, Preprocessor &PP) { // C++ [class.friend]p6: // No storage-class-specifier shall appear in the decl-specifier-seq // of a friend declaration. - if (isFriendSpecified() && getStorageClassSpec()) { - DeclSpec::SCS SC = getStorageClassSpec(); - const char *SpecName = getSpecifierName(SC); + if (isFriendSpecified() && + (getStorageClassSpec() || getThreadStorageClassSpec())) { + SmallString<32> SpecName; + SourceLocation SCLoc; + FixItHint StorageHint, ThreadHint; + + if (DeclSpec::SCS SC = getStorageClassSpec()) { + SpecName = getSpecifierName(SC); + SCLoc = getStorageClassSpecLoc(); + StorageHint = FixItHint::CreateRemoval(SCLoc); + } - SourceLocation SCLoc = getStorageClassSpecLoc(); - SourceLocation SCEndLoc = SCLoc.getLocWithOffset(strlen(SpecName)); + if (DeclSpec::TSCS TSC = getThreadStorageClassSpec()) { + if (!SpecName.empty()) SpecName += " "; + SpecName += getSpecifierName(TSC); + SCLoc = getThreadStorageClassSpecLoc(); + ThreadHint = FixItHint::CreateRemoval(SCLoc); + } Diag(D, SCLoc, diag::err_friend_storage_spec) - << SpecName - << FixItHint::CreateRemoval(SourceRange(SCLoc, SCEndLoc)); + << SpecName << StorageHint << ThreadHint; ClearStorageClassSpecs(); } diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index 2db1e2afa2..a72e74b5aa 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -3348,13 +3348,12 @@ void Sema::CodeCompleteDeclSpec(Scope *S, DeclSpec &DS, // be a receiver of a class message, this may be a class message send with // the initial opening bracket '[' missing. Add appropriate completions. if (AllowNonIdentifiers && !AllowNestedNameSpecifiers && + DS.getParsedSpecifiers() == DeclSpec::PQ_TypeSpecifier && DS.getTypeSpecType() == DeclSpec::TST_typename && - DS.getStorageClassSpec() == DeclSpec::SCS_unspecified && - !DS.isThreadSpecified() && !DS.isExternInLinkageSpec() && DS.getTypeSpecComplex() == DeclSpec::TSC_unspecified && DS.getTypeSpecSign() == DeclSpec::TSS_unspecified && - DS.getTypeQualifiers() == 0 && - S && + !DS.isTypeAltiVecVector() && + S && (S->getFlags() & Scope::DeclScope) != 0 && (S->getFlags() & (Scope::ClassScope | Scope::TemplateParamScope | Scope::FunctionPrototypeScope | diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index c8a5d932e7..6dfc86f501 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -3182,8 +3182,9 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS, Diag(DS.getStorageClassSpecLoc(), DiagID) << DeclSpec::getSpecifierName(SCS); - if (DS.isThreadSpecified()) - Diag(DS.getThreadSpecLoc(), DiagID) << "__thread"; + if (DeclSpec::TSCS TSCS = DS.getThreadStorageClassSpec()) + Diag(DS.getThreadStorageClassSpecLoc(), DiagID) + << DeclSpec::getSpecifierName(TSCS); if (DS.getTypeQualifiers()) { if (DS.getTypeQualifiers() & DeclSpec::TQ_const) Diag(DS.getConstSpecLoc(), DiagID) << "const"; @@ -4411,8 +4412,6 @@ Sema::ActOnTypedefDeclarator(Scope* S, Declarator& D, DeclContext* DC, DiagnoseFunctionSpecifiers(D.getDeclSpec()); - if (D.getDeclSpec().isThreadSpecified()) - Diag(D.getDeclSpec().getThreadSpecLoc(), diag::err_invalid_thread); if (D.getDeclSpec().isConstexprSpecified()) Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_invalid_constexpr) << 1; @@ -4871,12 +4870,17 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, // lexical context will be different from the semantic context. NewVD->setLexicalDeclContext(CurContext); - if (D.getDeclSpec().isThreadSpecified()) { + if (DeclSpec::TSCS TSCS = D.getDeclSpec().getThreadStorageClassSpec()) { if (NewVD->hasLocalStorage()) - Diag(D.getDeclSpec().getThreadSpecLoc(), diag::err_thread_non_global); + Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(), + diag::err_thread_non_global) + << DeclSpec::getSpecifierName(TSCS); else if (!Context.getTargetInfo().isTLSSupported()) - Diag(D.getDeclSpec().getThreadSpecLoc(), diag::err_thread_unsupported); + Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(), + diag::err_thread_unsupported); else + // FIXME: Track which thread specifier was used; they have different + // semantics. NewVD->setThreadSpecified(true); } @@ -5836,8 +5840,10 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, DeclarationName Name = NameInfo.getName(); FunctionDecl::StorageClass SC = getFunctionStorageClass(*this, D); - if (D.getDeclSpec().isThreadSpecified()) - Diag(D.getDeclSpec().getThreadSpecLoc(), diag::err_invalid_thread); + if (DeclSpec::TSCS TSCS = D.getDeclSpec().getThreadStorageClassSpec()) + Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(), + diag::err_invalid_thread) + << DeclSpec::getSpecifierName(TSCS); // Do not allow returning a objc interface by-value. if (R->getAs()->getResultType()->isObjCObjectType()) { @@ -8235,13 +8241,14 @@ Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D) { D.getMutableDeclSpec().ClearStorageClassSpecs(); } - if (D.getDeclSpec().isThreadSpecified()) - Diag(D.getDeclSpec().getThreadSpecLoc(), diag::err_invalid_thread); - if (D.getDeclSpec().isConstexprSpecified()) - Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_invalid_constexpr) + if (DeclSpec::TSCS TSCS = DS.getThreadStorageClassSpec()) + Diag(DS.getThreadStorageClassSpecLoc(), diag::err_invalid_thread) + << DeclSpec::getSpecifierName(TSCS); + if (DS.isConstexprSpecified()) + Diag(DS.getConstexprSpecLoc(), diag::err_invalid_constexpr) << 0; - DiagnoseFunctionSpecifiers(D.getDeclSpec()); + DiagnoseFunctionSpecifiers(DS); TypeSourceInfo *TInfo = GetTypeForDeclarator(D, S); QualType parmDeclType = TInfo->getType(); @@ -10361,8 +10368,10 @@ FieldDecl *Sema::HandleField(Scope *S, RecordDecl *Record, DiagnoseFunctionSpecifiers(D.getDeclSpec()); - if (D.getDeclSpec().isThreadSpecified()) - Diag(D.getDeclSpec().getThreadSpecLoc(), diag::err_invalid_thread); + if (DeclSpec::TSCS TSCS = D.getDeclSpec().getThreadStorageClassSpec()) + Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(), + diag::err_invalid_thread) + << DeclSpec::getSpecifierName(TSCS); // Check to see if this name was declared as a member previously NamedDecl *PrevDecl = 0; diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 0bea565f93..8e8ea01395 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -1679,30 +1679,24 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, // data members and cannot be applied to names declared const or static, // and cannot be applied to reference members. switch (DS.getStorageClassSpec()) { - case DeclSpec::SCS_unspecified: - case DeclSpec::SCS_typedef: - case DeclSpec::SCS_static: - // FALL THROUGH. - break; - case DeclSpec::SCS_mutable: - if (isFunc) { - if (DS.getStorageClassSpecLoc().isValid()) - Diag(DS.getStorageClassSpecLoc(), diag::err_mutable_function); - else - Diag(DS.getThreadSpecLoc(), diag::err_mutable_function); + case DeclSpec::SCS_unspecified: + case DeclSpec::SCS_typedef: + case DeclSpec::SCS_static: + break; + case DeclSpec::SCS_mutable: + if (isFunc) { + Diag(DS.getStorageClassSpecLoc(), diag::err_mutable_function); - // FIXME: It would be nicer if the keyword was ignored only for this - // declarator. Otherwise we could get follow-up errors. - D.getMutableDeclSpec().ClearStorageClassSpecs(); - } - break; - default: - if (DS.getStorageClassSpecLoc().isValid()) - Diag(DS.getStorageClassSpecLoc(), - diag::err_storageclass_invalid_for_member); - else - Diag(DS.getThreadSpecLoc(), diag::err_storageclass_invalid_for_member); + // FIXME: It would be nicer if the keyword was ignored only for this + // declarator. Otherwise we could get follow-up errors. D.getMutableDeclSpec().ClearStorageClassSpecs(); + } + break; + default: + Diag(DS.getStorageClassSpecLoc(), + diag::err_storageclass_invalid_for_member); + D.getMutableDeclSpec().ClearStorageClassSpecs(); + break; } bool isInstField = ((DS.getStorageClassSpec() == DeclSpec::SCS_unspecified || diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index cb5de9c84a..48f0decc2c 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -3190,12 +3190,14 @@ Decl *Sema::ActOnObjCExceptionDecl(Scope *S, Declarator &D) { if (DS.getStorageClassSpec() == DeclSpec::SCS_register) { Diag(DS.getStorageClassSpecLoc(), diag::warn_register_objc_catch_parm) << FixItHint::CreateRemoval(SourceRange(DS.getStorageClassSpecLoc())); - } else if (DS.getStorageClassSpec() != DeclSpec::SCS_unspecified) { + } else if (DeclSpec::SCS SCS = DS.getStorageClassSpec()) { Diag(DS.getStorageClassSpecLoc(), diag::err_storage_spec_on_catch_parm) - << DS.getStorageClassSpec(); - } - if (D.getDeclSpec().isThreadSpecified()) - Diag(D.getDeclSpec().getThreadSpecLoc(), diag::err_invalid_thread); + << DeclSpec::getSpecifierName(SCS); + } + if (DeclSpec::TSCS TSCS = D.getDeclSpec().getThreadStorageClassSpec()) + Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(), + diag::err_invalid_thread) + << DeclSpec::getSpecifierName(TSCS); D.getMutableDeclSpec().ClearStorageClassSpecs(); DiagnoseFunctionSpecifiers(D.getDeclSpec()); diff --git a/test/CXX/class/class.friend/p6.cpp b/test/CXX/class/class.friend/p6.cpp index 7d7a06419a..82ca50e485 100644 --- a/test/CXX/class/class.friend/p6.cpp +++ b/test/CXX/class/class.friend/p6.cpp @@ -1,10 +1,18 @@ -// RUN: %clang_cc1 -fsyntax-only -Wc++11-compat -verify %s +// RUN: %clang_cc1 -fsyntax-only -Wc++11-compat -verify -std=c++98 %s +// RUN: %clang_cc1 -fsyntax-only -Wc++11-compat -verify -std=c++11 %s class A { friend static class B; // expected-error {{'static' is invalid in friend declarations}} friend extern class C; // expected-error {{'extern' is invalid in friend declarations}} - friend auto class D; // expected-warning {{incompatible with C++11}} expected-error {{'auto' is invalid in friend declarations}} friend register class E; // expected-error {{'register' is invalid in friend declarations}} friend mutable class F; // expected-error {{'mutable' is invalid in friend declarations}} friend typedef class G; // expected-error {{'typedef' is invalid in friend declarations}} + friend __thread class G; // expected-error {{'__thread' is invalid in friend declarations}} + friend _Thread_local class G; // expected-error {{'_Thread_local' is invalid in friend declarations}} + friend static _Thread_local class G; // expected-error {{'static _Thread_local' is invalid in friend declarations}} +#if __cplusplus < 201103L + friend auto class D; // expected-warning {{incompatible with C++11}} expected-error {{'auto' is invalid in friend declarations}} +#else + friend thread_local class G; // expected-error {{'thread_local' is invalid in friend declarations}} +#endif }; diff --git a/test/Parser/cxx0x-decl.cpp b/test/Parser/cxx0x-decl.cpp index b9441fd681..c91cc9eb76 100644 --- a/test/Parser/cxx0x-decl.cpp +++ b/test/Parser/cxx0x-decl.cpp @@ -53,9 +53,8 @@ constexpr const int &ConstexprTrailingReturn::f() const { return n; } namespace TestIsValidAfterTypeSpecifier { struct s {} v; -// FIXME: We should accept this once we support thread_local. struct s -thread_local tl; // expected-error {{expected unqualified-id}} +thread_local tl; struct s &r0 = v; diff --git a/test/Sema/thread-specifier.c b/test/Sema/thread-specifier.c index 8c40fcd0a6..ca6c41192b 100644 --- a/test/Sema/thread-specifier.c +++ b/test/Sema/thread-specifier.c @@ -1,30 +1,78 @@ -// RUN: %clang_cc1 -triple i686-pc-linux-gnu -fsyntax-only -Wno-private-extern -verify -pedantic %s +// RUN: %clang_cc1 -triple i686-pc-linux-gnu -fsyntax-only -Wno-private-extern -verify -pedantic %s -DGNU +// RUN: %clang_cc1 -triple i686-pc-linux-gnu -fsyntax-only -Wno-private-extern -verify -pedantic -x c++ %s -DGNU +// 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 + +#ifdef __cplusplus +// In C++, we define __private_extern__ to extern. +#undef __private_extern__ +#endif __thread int t1; -__thread extern int t2; // expected-warning {{'__thread' before 'extern'}} -__thread static int t3; // expected-warning {{'__thread' before 'static'}} +__thread extern int t2; +__thread static int t3; +#ifdef GNU +// expected-warning@-3 {{'__thread' before 'extern'}} +// expected-warning@-3 {{'__thread' before 'static'}} +#endif + __thread __private_extern__ int t4; -struct t5 { __thread int x; }; // expected-error {{type name does not allow storage class to be specified}} -__thread int t6(); // expected-error {{'__thread' is only allowed on variable declarations}} +struct t5 { __thread int x; }; +#ifdef __cplusplus +// expected-error-re@-2 {{'(__thread|_Thread_local|thread_local)' is only allowed on variable declarations}} +#else +// FIXME: The 'is only allowed on variable declarations' diagnostic is better here. +// expected-error@-5 {{type name does not allow storage class to be specified}} +#endif + +__thread int t6(); +#if defined(GNU) +// expected-error@-2 {{'__thread' is only allowed on variable declarations}} +#elif defined(C11) +// expected-error@-4 {{'_Thread_local' is only allowed on variable declarations}} +#else +// expected-error@-6 {{'thread_local' is only allowed on variable declarations}} +#endif -int f(__thread int t7) { // expected-error {{'__thread' is only allowed on variable declarations}} - __thread int t8; // expected-error {{'__thread' variables must have global storage}} +int f(__thread int t7) { // expected-error {{' is only allowed on variable declarations}} + __thread int t8; +#if defined(GNU) + // expected-error@-2 {{'__thread' variables must have global storage}} +#elif defined(C11) + // expected-error@-4 {{'_Thread_local' variables must have global storage}} +#else + // expected-error@-6 {{'thread_local' variables must have global storage}} +#endif extern __thread int t9; static __thread int t10; __thread __private_extern__ int t11; - __thread auto int t12; // expected-error {{'__thread' variables must have global storage}} - __thread register int t13; // expected-error {{'__thread' variables must have global storage}} +#if __cplusplus < 201103L + __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}} +#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}} } -__thread typedef int t14; // expected-error {{'__thread' is only allowed on variable declarations}} +__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}} -int t15; // expected-error {{non-thread-local declaration of 't15' follows thread-local declaration}} -int t16; // expected-note {{previous definition 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}} __thread int t16; // expected-error {{thread-local declaration of 't16' follows non-thread-local declaration}} // PR13720 __thread int thread_int; -int *thread_int_ptr = &thread_int; // expected-error{{initializer element is not a compile-time constant}} +int *thread_int_ptr = &thread_int; +#ifndef __cplusplus +// expected-error@-2 {{initializer element is not a compile-time constant}} +#endif void g() { int *p = &thread_int; // This is perfectly fine, though. } +#if __cplusplus >= 201103L +constexpr int *thread_int_ptr_2 = &thread_int; // expected-error {{must be initialized by a constant expression}} +#endif -- 2.40.0