From 570024a8d9b4a4aa4a35f077a0a65003dc7b71fe Mon Sep 17 00:00:00 2001 From: Eli Friedman Date: Thu, 5 Aug 2010 06:57:20 +0000 Subject: [PATCH] Implement #pragma GCC visibility. llvm-svn: 110315 --- clang/include/clang/AST/Attr.h | 7 +- .../clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/include/clang/Parse/Action.h | 8 +++ clang/include/clang/Parse/Parser.h | 1 + clang/lib/AST/AttrImpl.cpp | 2 +- clang/lib/CodeGen/CodeGenModule.cpp | 6 +- clang/lib/Frontend/PCHReaderDecl.cpp | 3 +- clang/lib/Frontend/PCHWriter.cpp | 1 + clang/lib/Parse/ParsePragma.cpp | 53 +++++++++++++++ clang/lib/Parse/ParsePragma.h | 9 +++ clang/lib/Parse/Parser.cpp | 5 ++ clang/lib/Sema/Sema.cpp | 3 +- clang/lib/Sema/Sema.h | 22 ++++++ clang/lib/Sema/SemaAttr.cpp | 67 +++++++++++++++++++ clang/lib/Sema/SemaDecl.cpp | 15 +++++ clang/lib/Sema/SemaDeclAttr.cpp | 2 +- clang/lib/Sema/SemaDeclCXX.cpp | 5 ++ 17 files changed, 202 insertions(+), 9 deletions(-) diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h index 5775b2be4781..3e9c45ad4a9a 100644 --- a/clang/include/clang/AST/Attr.h +++ b/clang/include/clang/AST/Attr.h @@ -540,12 +540,15 @@ public: }; private: VisibilityTypes VisibilityType; + bool FromPragma; public: - VisibilityAttr(VisibilityTypes v) : Attr(attr::Visibility), - VisibilityType(v) {} + VisibilityAttr(VisibilityTypes v, bool fp) : Attr(attr::Visibility), + VisibilityType(v), FromPragma(fp) {} VisibilityTypes getVisibility() const { return VisibilityType; } + bool isFromPragma() const { return FromPragma; } + virtual Attr *clone(ASTContext &C) const; // Implement isa/cast/dyncast/etc. diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 2ff8de310e32..bebc333f6d1a 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -988,7 +988,7 @@ def warn_transparent_union_attribute_zero_fields : Warning< "transparent_union attribute ignored">; def warn_attribute_type_not_supported : Warning< "'%0' attribute argument not supported: %1">; -def warn_attribute_unknown_visibility : Warning<"unknown visibility '%1'">; +def warn_attribute_unknown_visibility : Warning<"unknown visibility '%0'">; def err_unknown_machine_mode : Error<"unknown machine mode %0">; def err_unsupported_machine_mode : Error<"unsupported machine mode %0">; def err_mode_not_primitive : Error< diff --git a/clang/include/clang/Parse/Action.h b/clang/include/clang/Parse/Action.h index d5cbd0cde338..e1a8ffbb7a40 100644 --- a/clang/include/clang/Parse/Action.h +++ b/clang/include/clang/Parse/Action.h @@ -2734,6 +2734,14 @@ public: return; } + + /// ActOnPragmaVisibility - Called on well formed #pragma GCC visibility... . + virtual void ActOnPragmaVisibility(bool IsPush, const IdentifierInfo* VisType, + SourceLocation PragmaLoc) { + return; + } + + /// ActOnPragmaWeakID - Called on well formed #pragma weak ident. virtual void ActOnPragmaWeakID(IdentifierInfo* WeakName, SourceLocation PragmaLoc, diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 234752decb8d..9a166a1e98ef 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -110,6 +110,7 @@ class Parser { IdentifierInfo *Ident_pixel; llvm::OwningPtr AlignHandler; + llvm::OwningPtr GCCVisibilityHandler; llvm::OwningPtr OptionsHandler; llvm::OwningPtr PackHandler; llvm::OwningPtr UnusedHandler; diff --git a/clang/lib/AST/AttrImpl.cpp b/clang/lib/AST/AttrImpl.cpp index 4146248fb039..8992e4823d52 100644 --- a/clang/lib/AST/AttrImpl.cpp +++ b/clang/lib/AST/AttrImpl.cpp @@ -213,7 +213,7 @@ Attr *SentinelAttr::clone(ASTContext &C) const { } Attr *VisibilityAttr::clone(ASTContext &C) const { - return ::new (C) VisibilityAttr(VisibilityType); + return ::new (C) VisibilityAttr(VisibilityType, FromPragma); } Attr *OverloadableAttr::clone(ASTContext &C) const { diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index d2be5af14a4a..633840257993 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -190,9 +190,11 @@ CodeGenModule::getDeclVisibilityMode(const Decl *D) const { return LangOptions::Hidden; } - // This decl should have the same visibility as its parent. + // If this decl is contained in a class, it should have the same visibility + // as the parent class. if (const DeclContext *DC = D->getDeclContext()) - return getDeclVisibilityMode(cast(DC)); + if (DC->isRecord()) + return getDeclVisibilityMode(cast(DC)); return getLangOptions().getVisibilityMode(); } diff --git a/clang/lib/Frontend/PCHReaderDecl.cpp b/clang/lib/Frontend/PCHReaderDecl.cpp index 21beb42e93e5..7557950e66d9 100644 --- a/clang/lib/Frontend/PCHReaderDecl.cpp +++ b/clang/lib/Frontend/PCHReaderDecl.cpp @@ -1292,7 +1292,8 @@ Attr *PCHReader::ReadAttributes(llvm::BitstreamCursor &DeclsCursor) { case attr::Visibility: New = ::new (*Context) VisibilityAttr( - (VisibilityAttr::VisibilityTypes)Record[Idx++]); + (VisibilityAttr::VisibilityTypes)Record[Idx++], + (bool)Record[Idx++]); break; SIMPLE_ATTR(WarnUnusedResult); diff --git a/clang/lib/Frontend/PCHWriter.cpp b/clang/lib/Frontend/PCHWriter.cpp index d5750a1f5049..b2fd98476665 100644 --- a/clang/lib/Frontend/PCHWriter.cpp +++ b/clang/lib/Frontend/PCHWriter.cpp @@ -2106,6 +2106,7 @@ void PCHWriter::WriteAttributeRecord(const Attr *Attr) { case attr::Visibility: // FIXME: stable encoding Record.push_back(cast(Attr)->getVisibility()); + Record.push_back(cast(Attr)->isFromPragma()); break; case attr::WarnUnusedResult: diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp index e414e4fc5ae1..c53b2e4d27cc 100644 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -18,6 +18,59 @@ #include "clang/Parse/Parser.h" using namespace clang; + +// #pragma GCC visibility comes in two variants: +// 'push' '(' [visibility] ')' +// 'pop' +void PragmaGCCVisibilityHandler::HandlePragma(Preprocessor &PP, Token &VisTok) { + SourceLocation VisLoc = VisTok.getLocation(); + + Token Tok; + PP.Lex(Tok); + + const IdentifierInfo *PushPop = Tok.getIdentifierInfo(); + + bool IsPush; + const IdentifierInfo *VisType; + if (PushPop && PushPop->isStr("pop")) { + IsPush = false; + VisType = 0; + } else if (PushPop && PushPop->isStr("push")) { + IsPush = true; + PP.Lex(Tok); + if (Tok.isNot(tok::l_paren)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_lparen) + << "visibility"; + return; + } + PP.Lex(Tok); + VisType = Tok.getIdentifierInfo(); + if (!VisType) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier) + << "visibility"; + return; + } + PP.Lex(Tok); + if (Tok.isNot(tok::r_paren)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_rparen) + << "visibility"; + return; + } + } else { + PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier) + << "visibility"; + return; + } + PP.Lex(Tok); + if (Tok.isNot(tok::eom)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) + << "visibility"; + return; + } + + Actions.ActOnPragmaVisibility(IsPush, VisType, VisLoc); +} + // #pragma pack(...) comes in the following delicious flavors: // pack '(' [integer] ')' // pack '(' 'show' ')' diff --git a/clang/lib/Parse/ParsePragma.h b/clang/lib/Parse/ParsePragma.h index 1d66277d33e5..8aef4009689c 100644 --- a/clang/lib/Parse/ParsePragma.h +++ b/clang/lib/Parse/ParsePragma.h @@ -28,6 +28,15 @@ public: virtual void HandlePragma(Preprocessor &PP, Token &FirstToken); }; +class PragmaGCCVisibilityHandler : public PragmaHandler { + Action &Actions; +public: + explicit PragmaGCCVisibilityHandler(Action &A) : PragmaHandler("visibility"), + Actions(A) {} + + virtual void HandlePragma(Preprocessor &PP, Token &FirstToken); +}; + class PragmaOptionsHandler : public PragmaHandler { Action &Actions; public: diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 63aa2bf47d20..b32c1f8a1799 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -36,6 +36,9 @@ Parser::Parser(Preprocessor &pp, Action &actions) AlignHandler.reset(new PragmaAlignHandler(actions)); PP.AddPragmaHandler(AlignHandler.get()); + GCCVisibilityHandler.reset(new PragmaGCCVisibilityHandler(actions)); + PP.AddPragmaHandler("GCC", GCCVisibilityHandler.get()); + OptionsHandler.reset(new PragmaOptionsHandler(actions)); PP.AddPragmaHandler(OptionsHandler.get()); @@ -303,6 +306,8 @@ Parser::~Parser() { // Remove the pragma handlers we installed. PP.RemovePragmaHandler(AlignHandler.get()); AlignHandler.reset(); + PP.RemovePragmaHandler("GCC", GCCVisibilityHandler.get()); + GCCVisibilityHandler.reset(); PP.RemovePragmaHandler(OptionsHandler.get()); OptionsHandler.reset(); PP.RemovePragmaHandler(PackHandler.get()); diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index a42c6e804cf8..2c834f434928 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -126,7 +126,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, LangOpts(pp.getLangOptions()), PP(pp), Context(ctxt), Consumer(consumer), Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()), ExternalSource(0), CodeCompleter(CodeCompleter), CurContext(0), - PackContext(0), TopFunctionScope(0), ParsingDeclDepth(0), + PackContext(0), VisContext(0), TopFunctionScope(0), ParsingDeclDepth(0), IdResolver(pp.getLangOptions()), GlobalNewDeleteDeclared(false), CompleteTranslationUnit(CompleteTranslationUnit), NumSFINAEErrors(0), SuppressAccessChecking(false), @@ -147,6 +147,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, Sema::~Sema() { if (PackContext) FreePackedContext(); + if (VisContext) FreeVisContext(); delete TheTargetAttributesSema; while (!FunctionScopes.empty()) PopFunctionOrBlockScope(); diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 59a628aad222..967d4757da4b 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -285,6 +285,9 @@ public: /// of 0 indicates default alignment. void *PackContext; // Really a "PragmaPackStack*" + /// VisContext - Manages the stack for #pragma GCC visibility. + void *VisContext; // Really a "PragmaVisStack*" + /// \brief Stack containing information about each of the nested function, /// block, and method scopes that are currently active. llvm::SmallVector FunctionScopes; @@ -4199,6 +4202,10 @@ public: SourceLocation LParenLoc, SourceLocation RParenLoc); + /// ActOnPragmaVisibility - Called on well formed #pragma GCC visibility... . + virtual void ActOnPragmaVisibility(bool IsPush, const IdentifierInfo* VisType, + SourceLocation PragmaLoc); + NamedDecl *DeclClonePragmaWeak(NamedDecl *ND, IdentifierInfo *II); void DeclApplyPragmaWeak(Scope *S, NamedDecl *ND, WeakInfo &W); @@ -4221,6 +4228,21 @@ public: /// FreePackedContext - Deallocate and null out PackContext. void FreePackedContext(); + /// AddPushedVisibilityAttribute - If '#pragma GCC visibility' was used, + /// add an appropriate visibility attribute. + void AddPushedVisibilityAttribute(Decl *RD); + + /// PushPragmaVisibility - Push the top element of the visibility stack; used + /// for '#pragma GCC visibility' and visibility attributes on namespaces. + void PushPragmaVisibility(VisibilityAttr::VisibilityTypes type); + + /// PopPragmaVisibility - Pop the top element of the visibility stack; used + /// for '#pragma GCC visibility' and visibility attributes on namespaces. + void PopPragmaVisibility(); + + /// FreeVisContext - Deallocate and null out VisContext. + void FreeVisContext(); + /// AddAlignedAttr - Adds an aligned attribute to a particular declaration. void AddAlignedAttr(SourceLocation AttrLoc, Decl *D, Expr *E); diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp index 13a0c59bbdb5..d1e5fe64dfb3 100644 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -288,3 +288,70 @@ void Sema::ActOnPragmaUnused(const Token *Identifiers, unsigned NumIdentifiers, VD->addAttr(::new (Context) UnusedAttr()); } } + +typedef std::vector VisStack; + +void Sema::AddPushedVisibilityAttribute(Decl *D) { + if (!VisContext) + return; + + if (D->hasAttr()) + return; + + VisStack *Stack = static_cast(VisContext); + VisibilityAttr::VisibilityTypes type = Stack->back(); + + D->addAttr(::new (Context) VisibilityAttr(type, true)); +} + +/// FreeVisContext - Deallocate and null out VisContext. +void Sema::FreeVisContext() { + delete static_cast(VisContext); + VisContext = 0; +} + +void Sema::ActOnPragmaVisibility(bool IsPush, const IdentifierInfo* VisType, + SourceLocation PragmaLoc) { + if (IsPush) { + // Compute visibility to use. + VisibilityAttr::VisibilityTypes type; + if (VisType->isStr("default")) + type = VisibilityAttr::DefaultVisibility; + else if (VisType->isStr("hidden")) + type = VisibilityAttr::HiddenVisibility; + else if (VisType->isStr("internal")) + type = VisibilityAttr::HiddenVisibility; // FIXME + else if (VisType->isStr("protected")) + type = VisibilityAttr::ProtectedVisibility; + else { + Diag(PragmaLoc, diag::warn_attribute_unknown_visibility) << + VisType->getName(); + return; + } + PushPragmaVisibility(type); + } else { + PopPragmaVisibility(); + } +} + +void Sema::PushPragmaVisibility(VisibilityAttr::VisibilityTypes type) { + // Put visibility on stack. + if (!VisContext) + VisContext = new VisStack; + + VisStack *Stack = static_cast(VisContext); + Stack->push_back(type); +} + +void Sema::PopPragmaVisibility() { + // Pop visibility from stack, if there is one on the stack. + if (VisContext) { + VisStack *Stack = static_cast(VisContext); + + Stack->pop_back(); + // To simplify the implementation, never keep around an empty stack. + if (Stack->empty()) + FreeVisContext(); + } + // FIXME: Add diag for pop without push. +} diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index e166e9cc2dc6..a1580f14dc52 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2680,6 +2680,11 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC, !NewVD->isInvalidDecl()) RegisterLocallyScopedExternCDecl(NewVD, Previous, S); + // If there's a #pragma GCC visibility in scope, and this isn't a class + // member, set the visibility of this variable. + if (NewVD->getLinkage() == ExternalLinkage && !DC->isRecord()) + AddPushedVisibilityAttribute(NewVD); + return NewVD; } @@ -3529,6 +3534,11 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, NewFD->addAttr(::new (Context) OverloadableAttr()); } + // If there's a #pragma GCC visibility in scope, and this isn't a class + // member, set the visibility of this function. + if (NewFD->getLinkage() == ExternalLinkage && !DC->isRecord()) + AddPushedVisibilityAttribute(NewFD); + // If this is a locally-scoped extern C function, update the // map of such names. if (CurContext->isFunctionOrMethod() && NewFD->isExternC() @@ -6367,6 +6377,11 @@ void Sema::ActOnFields(Scope* S, if (Attr) ProcessDeclAttributeList(S, Record, Attr); + + // If there's a #pragma GCC visibility in scope, and this isn't a subclass, + // set the visibility of this record. + if (Record && !Record->getDeclContext()->isRecord()) + AddPushedVisibilityAttribute(Record); } /// \brief Determine whether the given integral value is representable within diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index cebe6e7850a1..2af3102ad759 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -847,7 +847,7 @@ static void HandleVisibilityAttr(Decl *d, const AttributeList &Attr, Sema &S) { return; } - d->addAttr(::new (S.Context) VisibilityAttr(type)); + d->addAttr(::new (S.Context) VisibilityAttr(type, false)); } static void HandleObjCExceptionAttr(Decl *D, const AttributeList &Attr, diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 9a1a4cceced0..bd7c47683efd 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -3179,6 +3179,9 @@ Sema::DeclPtrTy Sema::ActOnStartNamespaceDef(Scope *NamespcScope, ProcessDeclAttributeList(DeclRegionScope, Namespc, AttrList); + if (const VisibilityAttr *attr = Namespc->getAttr()) + PushPragmaVisibility(attr->getVisibility()); + if (II) { // C++ [namespace.def]p2: // The identifier in an original-namespace-definition shall not have been @@ -3310,6 +3313,8 @@ void Sema::ActOnFinishNamespaceDef(DeclPtrTy D, SourceLocation RBrace) { assert(Namespc && "Invalid parameter, expected NamespaceDecl"); Namespc->setRBracLoc(RBrace); PopDeclContext(); + if (Namespc->hasAttr()) + PopPragmaVisibility(); } /// \brief Retrieve the special "std" namespace, which may require us to