[cfe-commits] r45716 - in /cfe/trunk: Lex/Preprocessor.cpp include/clang/Lex/MultipleIncludeOpt.h test/Preprocessor/mi_opt.c test/Preprocessor/mi_opt.h

Chris Lattner sabre at nondot.org
Mon Jan 7 11:50:28 PST 2008


Author: lattner
Date: Mon Jan  7 13:50:27 2008
New Revision: 45716

URL: http://llvm.org/viewvc/llvm-project?rev=45716&view=rev
Log:
Fix a nasty corner case that Neil noticed in PR1900, where we would 
incorrectly apply the multiple include optimization to files with 
guards like:

#if !defined(x) MACRO

where MACRO could expand to different things in different contexts.
Thanks Neil!


Added:
    cfe/trunk/test/Preprocessor/mi_opt.c
    cfe/trunk/test/Preprocessor/mi_opt.h
Modified:
    cfe/trunk/Lex/Preprocessor.cpp
    cfe/trunk/include/clang/Lex/MultipleIncludeOpt.h

Modified: cfe/trunk/Lex/Preprocessor.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/Lex/Preprocessor.cpp?rev=45716&r1=45715&r2=45716&view=diff

==============================================================================
--- cfe/trunk/Lex/Preprocessor.cpp (original)
+++ cfe/trunk/Lex/Preprocessor.cpp Mon Jan  7 13:50:27 2008
@@ -784,6 +784,10 @@
 /// expanded as a macro, handle it and return the next token as 'Identifier'.
 bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier, 
                                                  MacroInfo *MI) {
+  // If this is a macro exapnsion in the "#if !defined(x)" line for the file,
+  // then the macro could expand to different things in other contexts, we need
+  // to disable the optimization in this case.
+  if (CurLexer) CurLexer->MIOpt.ExpandedMacro();
   
   // If this is a builtin macro, like __LINE__ or _Pragma, handle it specially.
   if (MI->isBuiltinMacro()) {

Modified: cfe/trunk/include/clang/Lex/MultipleIncludeOpt.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Lex/MultipleIncludeOpt.h?rev=45716&r1=45715&r2=45716&view=diff

==============================================================================
--- cfe/trunk/include/clang/Lex/MultipleIncludeOpt.h (original)
+++ cfe/trunk/include/clang/Lex/MultipleIncludeOpt.h Mon Jan  7 13:50:27 2008
@@ -29,12 +29,23 @@
   /// to false, that way any tokens before the first #ifdef or after the last
   /// #endif can be easily detected.
   bool ReadAnyTokens;
+
+  /// ReadAnyTokens - This is set to false when a file is first opened and true
+  /// any time a token is returned to the client or a (non-multiple-include)
+  /// directive is parsed.  When the final #endif is parsed this is reset back
+  /// to false, that way any tokens before the first #ifdef or after the last
+  /// #endif can be easily detected.
+  bool DidMacroExpansion;
   
   /// TheMacro - The controlling macro for a file, if valid.
   ///
   const IdentifierInfo *TheMacro;
 public:
-  MultipleIncludeOpt() : ReadAnyTokens(false), TheMacro(0) {}
+  MultipleIncludeOpt() {
+    ReadAnyTokens = false;
+    DidMacroExpansion = false;
+    TheMacro = 0;
+  }
   
   /// Invalidate - Permenantly mark this file as not being suitable for the
   /// include-file optimization.
@@ -53,18 +64,30 @@
   // If a token is read, remember that we have seen a side-effect in this file.
   void ReadToken() { ReadAnyTokens = true; }
   
+  /// ExpandedMacro - When a macro is expanded with this lexer as the current
+  /// buffer, this method is called to disable the MIOpt if needed.
+  void ExpandedMacro() { DidMacroExpansion = true; }
+  
   /// EnterTopLevelIFNDEF - When entering a top-level #ifndef directive (or the
   /// "#if !defined" equivalent) without any preceding tokens, this method is
   /// called.
+  ///
+  /// Note, we don't care about the input value of 'ReadAnyTokens'.  The caller
+  /// ensures that this is only called if there are no tokens read before the
+  /// #ifndef.  The caller is required to do this, because reading the #if line
+  /// obviously reads in in tokens.
   void EnterTopLevelIFNDEF(const IdentifierInfo *M) {
-    // Note, we don't care about the input value of 'ReadAnyTokens'.  The caller
-    // ensures that this is only called if there are no tokens read before the
-    // #ifndef.
-    
     // If the macro is already set, this is after the top-level #endif.
     if (TheMacro)
       return Invalidate();
     
+    // If we have already expanded a macro by the end of the #ifndef line, then
+    // there is a macro expansion *in* the #ifndef line.  This means that the
+    // condition could evaluate differently when subsequently #included.  Reject
+    // this.
+    if (DidMacroExpansion)
+      return Invalidate();
+    
     // Remember that we're in the #if and that we have the macro.
     ReadAnyTokens = true;
     TheMacro = M;

Added: cfe/trunk/test/Preprocessor/mi_opt.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Preprocessor/mi_opt.c?rev=45716&view=auto

==============================================================================
--- cfe/trunk/test/Preprocessor/mi_opt.c (added)
+++ cfe/trunk/test/Preprocessor/mi_opt.c Mon Jan  7 13:50:27 2008
@@ -0,0 +1,11 @@
+// RUN: not clang -fsyntax-only %s
+// PR1900
+// This test should get a redefinition error from m_iopt.h: the MI opt 
+// shouldn't apply.
+
+#define MACRO
+#include "mi_opt.h"
+#undef MACRO
+#define MACRO || 1
+#include "mi_opt.h"
+

Added: cfe/trunk/test/Preprocessor/mi_opt.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Preprocessor/mi_opt.h?rev=45716&view=auto

==============================================================================
--- cfe/trunk/test/Preprocessor/mi_opt.h (added)
+++ cfe/trunk/test/Preprocessor/mi_opt.h Mon Jan  7 13:50:27 2008
@@ -0,0 +1,4 @@
+#if !defined foo MACRO
+#define foo
+int x = 2;
+#endif





More information about the cfe-commits mailing list