From: Chris Lattner Date: Tue, 17 Aug 2010 15:55:45 +0000 (+0000) Subject: Implement #pragma push_macro, patch by Francois Pichet! X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f47724bf78299c7a50f008e0443c5f9f9f279ddc;p=clang Implement #pragma push_macro, patch by Francois Pichet! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@111234 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td index 9b06fa80ba..5068a055e8 100644 --- a/include/clang/Basic/DiagnosticLexKinds.td +++ b/include/clang/Basic/DiagnosticLexKinds.td @@ -230,6 +230,10 @@ def err_pragma_comment_malformed : Error< "pragma comment requires parenthesized identifier and optional string">; def err_pragma_message_malformed : Error< "pragma message requires parenthesized string">; +def err_pragma_push_pop_macro_malformed : Error< + "pragma %0 requires a parenthesized string">; +def warn_pragma_pop_macro_no_push : Warning< + "pragma pop_macro could not pop '%0', no matching push_macro">; def warn_pragma_message : Warning<"%0">; def warn_pragma_ignored : Warning<"unknown pragma ignored">, InGroup, DefaultIgnore; diff --git a/include/clang/Lex/MacroInfo.h b/include/clang/Lex/MacroInfo.h index 6d5896c2d5..13dd40a516 100644 --- a/include/clang/Lex/MacroInfo.h +++ b/include/clang/Lex/MacroInfo.h @@ -79,13 +79,18 @@ private: /// emit -Wunused-macros diagnostics. bool IsUsed : 1; - ~MacroInfo() { + /// AllowRedefinitionsWithoutWarning - True if this macro can be redefined + /// without emitting a warning. + bool IsAllowRedefinitionsWithoutWarning : 1; + + ~MacroInfo() { assert(ArgumentList == 0 && "Didn't call destroy before dtor!"); } public: MacroInfo(SourceLocation DefLoc); - + MacroInfo(const MacroInfo &MI, llvm::BumpPtrAllocator &PPAllocator); + /// FreeArgumentList - Free the argument list of the macro, restoring it to a /// state where it can be reused for other devious purposes. void FreeArgumentList(llvm::BumpPtrAllocator &PPAllocator) { @@ -128,6 +133,12 @@ public: IsUsed = Val; } + /// setIsAllowRedefinitionsWithoutWarning - Set the value of the + /// IsAllowRedefinitionsWithoutWarning flag. + void setIsAllowRedefinitionsWithoutWarning(bool Val) { + IsAllowRedefinitionsWithoutWarning = Val; + } + /// setArgumentList - Set the specified list of identifiers as the argument /// list for this macro. void setArgumentList(IdentifierInfo* const *List, unsigned NumArgs, @@ -185,6 +196,12 @@ public: /// not yet been used. bool isUsed() const { return IsUsed; } + /// isAllowRedefinitionsWithoutWarning - Return true if this macro can be + /// redefined without warning. + bool isAllowRedefinitionsWithoutWarning() const { + return IsAllowRedefinitionsWithoutWarning; + } + /// getNumTokens - Return the number of tokens that this macro expands to. /// unsigned getNumTokens() const { diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index 6fd19437f8..8bf0df3d02 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -198,6 +198,11 @@ class Preprocessor { /// reused for quick allocation. MacroArgs *MacroArgCache; friend class MacroArgs; + + /// PragmaPushMacroInfo - For each IdentifierInfo used in a #pragma + /// push_macro directive, we keep a MacroInfo stack used to restore + /// previous macro value. + llvm::DenseMap > PragmaPushMacroInfo; // Various statistics we track for performance analysis. unsigned NumDirectives, NumIncluded, NumDefined, NumUndefined, NumPragma; @@ -744,7 +749,10 @@ public: /// AllocateMacroInfo - Allocate a new MacroInfo object with the provide /// SourceLocation. - MacroInfo* AllocateMacroInfo(SourceLocation L); + MacroInfo *AllocateMacroInfo(SourceLocation L); + + /// CloneMacroInfo - Allocate a new MacroInfo object which is clone of MI. + MacroInfo *CloneMacroInfo(const MacroInfo &MI); /// GetIncludeFilenameSpelling - Turn the specified lexer token into a fully /// checked and spelled filename, e.g. as an operand of #include. This returns @@ -802,6 +810,9 @@ private: IncludeMacroStack.pop_back(); } + /// AllocateMacroInfo - Allocate a new MacroInfo object. + MacroInfo *AllocateMacroInfo(); + /// ReleaseMacroInfo - Release the specified MacroInfo. This memory will /// be reused for allocating new MacroInfo objects. void ReleaseMacroInfo(MacroInfo* MI); @@ -948,6 +959,10 @@ public: void HandlePragmaDependency(Token &DependencyTok); void HandlePragmaComment(Token &CommentTok); void HandlePragmaMessage(Token &MessageTok); + void HandlePragmaPushMacro(Token &Tok); + void HandlePragmaPopMacro(Token &Tok); + IdentifierInfo *ParsePragmaPushOrPopMacro(Token &Tok); + // Return true and store the first token only if any CommentHandler // has inserted some tokens and getCommentRetentionState() is false. bool HandleComment(Token &Token, SourceRange Comment); diff --git a/lib/Lex/MacroInfo.cpp b/lib/Lex/MacroInfo.cpp index 7a09986e3d..6eac0364f6 100644 --- a/lib/Lex/MacroInfo.cpp +++ b/lib/Lex/MacroInfo.cpp @@ -23,11 +23,29 @@ MacroInfo::MacroInfo(SourceLocation DefLoc) : Location(DefLoc) { IsFromPCH = false; IsDisabled = false; IsUsed = true; + IsAllowRedefinitionsWithoutWarning = false; ArgumentList = 0; NumArguments = 0; } +MacroInfo::MacroInfo(const MacroInfo &MI, llvm::BumpPtrAllocator &PPAllocator) { + Location = MI.Location; + EndLocation = MI.EndLocation; + ReplacementTokens = MI.ReplacementTokens; + IsFunctionLike = MI.IsFunctionLike; + IsC99Varargs = MI.IsC99Varargs; + IsGNUVarargs = MI.IsGNUVarargs; + IsBuiltinMacro = MI.IsBuiltinMacro; + IsFromPCH = MI.IsFromPCH; + IsDisabled = MI.IsDisabled; + IsUsed = MI.IsUsed; + IsAllowRedefinitionsWithoutWarning = MI.IsAllowRedefinitionsWithoutWarning; + ArgumentList = 0; + NumArguments = 0; + setArgumentList(MI.ArgumentList, MI.NumArguments, PPAllocator); +} + /// isIdenticalTo - Return true if the specified macro definition is equal to /// this macro in spelling, arguments, and whitespace. This is used to emit /// duplicate definition warnings. This implements the rules in C99 6.10.3. diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp index 9cab2d2bfc..cd24533eb2 100644 --- a/lib/Lex/PPDirectives.cpp +++ b/lib/Lex/PPDirectives.cpp @@ -25,7 +25,7 @@ using namespace clang; // Utility Methods for Preprocessor Directive Handling. //===----------------------------------------------------------------------===// -MacroInfo *Preprocessor::AllocateMacroInfo(SourceLocation L) { +MacroInfo *Preprocessor::AllocateMacroInfo() { MacroInfo *MI; if (!MICache.empty()) { @@ -33,10 +33,21 @@ MacroInfo *Preprocessor::AllocateMacroInfo(SourceLocation L) { MICache.pop_back(); } else MI = (MacroInfo*) BP.Allocate(); + return MI; +} + +MacroInfo *Preprocessor::AllocateMacroInfo(SourceLocation L) { + MacroInfo *MI = AllocateMacroInfo(); new (MI) MacroInfo(L); return MI; } +MacroInfo *Preprocessor::CloneMacroInfo(const MacroInfo &MacroToClone) { + MacroInfo *MI = AllocateMacroInfo(); + new (MI) MacroInfo(MacroToClone, BP); + return MI; +} + /// ReleaseMacroInfo - Release the specified MacroInfo. This memory will /// be reused for allocating new MacroInfo objects. void Preprocessor::ReleaseMacroInfo(MacroInfo* MI) { @@ -1446,15 +1457,15 @@ void Preprocessor::HandleDefineDirective(Token &DefineTok) { if (!OtherMI->isUsed()) Diag(OtherMI->getDefinitionLoc(), diag::pp_macro_not_used); - // Macros must be identical. This means all tokes and whitespace + // Macros must be identical. This means all tokens and whitespace // separation must be the same. C99 6.10.3.2. - if (!MI->isIdenticalTo(*OtherMI, *this)) { + if (!OtherMI->isAllowRedefinitionsWithoutWarning() && + !MI->isIdenticalTo(*OtherMI, *this)) { Diag(MI->getDefinitionLoc(), diag::ext_pp_macro_redef) << MacroNameTok.getIdentifierInfo(); Diag(OtherMI->getDefinitionLoc(), diag::note_previous_definition); } } - ReleaseMacroInfo(OtherMI); } diff --git a/lib/Lex/Pragma.cpp b/lib/Lex/Pragma.cpp index 783900de46..eec1d1d6ee 100644 --- a/lib/Lex/Pragma.cpp +++ b/lib/Lex/Pragma.cpp @@ -16,6 +16,7 @@ #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/LiteralSupport.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Lex/MacroInfo.h" #include "clang/Lex/LexDiagnostic.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" @@ -483,6 +484,109 @@ void Preprocessor::HandlePragmaMessage(Token &Tok) { Callbacks->PragmaMessage(MessageLoc, MessageString); } +/// ParsePragmaPushOrPopMacro - Handle parsing of pragma push_macro/pop_macro. +/// Return the IdentifierInfo* associated with the macro to push or pop. +IdentifierInfo *Preprocessor::ParsePragmaPushOrPopMacro(Token &Tok) { + // Remember the pragma token location. + Token PragmaTok = Tok; + + // Read the '('. + Lex(Tok); + if (Tok.isNot(tok::l_paren)) { + Diag(PragmaTok.getLocation(), diag::err_pragma_push_pop_macro_malformed) + << getSpelling(PragmaTok); + return 0; + } + + // Read the macro name string. + Lex(Tok); + if (Tok.isNot(tok::string_literal)) { + Diag(PragmaTok.getLocation(), diag::err_pragma_push_pop_macro_malformed) + << getSpelling(PragmaTok); + return 0; + } + + // Remember the macro string. + std::string StrVal = getSpelling(Tok); + + // Read the ')'. + Lex(Tok); + if (Tok.isNot(tok::r_paren)) { + Diag(PragmaTok.getLocation(), diag::err_pragma_push_pop_macro_malformed) + << getSpelling(PragmaTok); + return 0; + } + + assert(StrVal[0] == '"' && StrVal[StrVal.size()-1] == '"' && + "Invalid string token!"); + + // Create a Token from the string. + Token MacroTok; + MacroTok.startToken(); + MacroTok.setKind(tok::identifier); + CreateString(&StrVal[1], StrVal.size() - 2, MacroTok); + + // Get the IdentifierInfo of MacroToPushTok. + return LookUpIdentifierInfo(MacroTok); +} + +/// HandlePragmaPushMacro - Handle #pragma push_macro. +/// The syntax is: +/// #pragma push_macro("macro") +void Preprocessor::HandlePragmaPushMacro(Token &PushMacroTok) { + // Parse the pragma directive and get the macro IdentifierInfo*. + IdentifierInfo *IdentInfo = ParsePragmaPushOrPopMacro(PushMacroTok); + if (!IdentInfo) return; + + // Get the MacroInfo associated with IdentInfo. + MacroInfo *MI = getMacroInfo(IdentInfo); + + MacroInfo *MacroCopyToPush = 0; + if (MI) { + // Make a clone of MI. + MacroCopyToPush = CloneMacroInfo(*MI); + + // Allow the original MacroInfo to be redefined later. + MI->setIsAllowRedefinitionsWithoutWarning(true); + } + + // Push the cloned MacroInfo so we can retrieve it later. + PragmaPushMacroInfo[IdentInfo].push_back(MacroCopyToPush); +} + +/// HandlePragmaPopMacro - Handle #pragma push_macro. +/// The syntax is: +/// #pragma pop_macro("macro") +void Preprocessor::HandlePragmaPopMacro(Token &PopMacroTok) { + SourceLocation MessageLoc = PopMacroTok.getLocation(); + + // Parse the pragma directive and get the macro IdentifierInfo*. + IdentifierInfo *IdentInfo = ParsePragmaPushOrPopMacro(PopMacroTok); + if (!IdentInfo) return; + + // Find the vector associated with the macro. + llvm::DenseMap >::iterator iter = + PragmaPushMacroInfo.find(IdentInfo); + if (iter != PragmaPushMacroInfo.end()) { + // Release the MacroInfo currently associated with IdentInfo. + MacroInfo *CurrentMI = getMacroInfo(IdentInfo); + if (CurrentMI) ReleaseMacroInfo(CurrentMI); + + // Get the MacroInfo we want to reinstall. + MacroInfo *MacroToReInstall = iter->second.back(); + + // Reinstall the previously pushed macro. + setMacroInfo(IdentInfo, MacroToReInstall); + + // Pop PragmaPushMacroInfo stack. + iter->second.pop_back(); + if (iter->second.size() == 0) + PragmaPushMacroInfo.erase(iter); + } else { + Diag(MessageLoc, diag::warn_pragma_pop_macro_no_push) + << IdentInfo->getName(); + } +} /// AddPragmaHandler - Add the specified pragma handler to the preprocessor. /// If 'Namespace' is non-null, then it is a token required to exist on the @@ -726,6 +830,25 @@ struct PragmaMessageHandler : public PragmaHandler { } }; +/// PragmaPushMacroHandler - "#pragma push_macro" saves the value of the +/// macro on the top of the stack. +struct PragmaPushMacroHandler : public PragmaHandler { + PragmaPushMacroHandler() : PragmaHandler("push_macro") {} + virtual void HandlePragma(Preprocessor &PP, Token &PushMacroTok) { + PP.HandlePragmaPushMacro(PushMacroTok); + } +}; + + +/// PragmaPopMacroHandler - "#pragma pop_macro" sets the value of the +/// macro to the value on the top of the stack. +struct PragmaPopMacroHandler : public PragmaHandler { + PragmaPopMacroHandler() : PragmaHandler("pop_macro") {} + virtual void HandlePragma(Preprocessor &PP, Token &PopMacroTok) { + PP.HandlePragmaPopMacro(PopMacroTok); + } +}; + // Pragma STDC implementations. enum STDCSetting { @@ -807,6 +930,8 @@ struct PragmaSTDC_UnknownHandler : public PragmaHandler { void Preprocessor::RegisterBuiltinPragmas() { AddPragmaHandler(new PragmaOnceHandler()); AddPragmaHandler(new PragmaMarkHandler()); + AddPragmaHandler(new PragmaPushMacroHandler()); + AddPragmaHandler(new PragmaPopMacroHandler()); // #pragma GCC ... AddPragmaHandler("GCC", new PragmaPoisonHandler()); diff --git a/test/Preprocessor/pragma-pushpop-macro.c b/test/Preprocessor/pragma-pushpop-macro.c new file mode 100644 index 0000000000..87cceaa419 --- /dev/null +++ b/test/Preprocessor/pragma-pushpop-macro.c @@ -0,0 +1,33 @@ +/* Test pragma pop_macro and push_macro directives from + http://msdn.microsoft.com/en-us/library/hsttss76.aspx */ + +// pop_macro: Sets the value of the macro_name macro to the value on the top of +// the stack for this macro. +// #pragma pop_macro("macro_name") +// push_macro: Saves the value of the macro_name macro on the top of the stack +// for this macro. +// #pragma push_macro("macro_name") +// +// RUN: %clang_cc1 -fms-extensions -E %s -o - | FileCheck %s + +#define X 1 +#define Y 2 +int pmx0 = X; +int pmy0 = Y; +#define Y 3 +#pragma push_macro("Y") +#pragma push_macro("X") +int pmx1 = X; +#define X 2 +int pmx2 = X; +#pragma pop_macro("X") +int pmx3 = X; +#pragma pop_macro("Y") +int pmy1 = Y; + +// CHECK: int pmx0 = 1 +// CHECK: int pmy0 = 2 +// CHECK: int pmx1 = 1 +// CHECK: int pmx2 = 2 +// CHECK: int pmx3 = 1 +// CHECK: int pmy1 = 3