]> granicus.if.org Git - clang/commitdiff
[modules] Add -fmodules-local-submodule-visibility flag.
authorRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 1 May 2015 21:22:17 +0000 (21:22 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 1 May 2015 21:22:17 +0000 (21:22 +0000)
This flag specifies that the normal visibility rules should be used even for
local submodules (submodules of the currently-being-built module). Thus names
will only be visible if a header / module that declares them has actually been
included / imported, and not merely because a submodule that happened to be
built earlier declared those names. This also removes the need to modularize
bottom-up: textually-included headers will be included into every submodule
that includes them, since their include guards will not leak between modules.

So far, this only governs visibility of macros, not of declarations, so is not
ready for real use yet.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@236350 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/LangOptions.def
include/clang/Basic/Module.h
include/clang/Driver/CC1Options.td
include/clang/Lex/Preprocessor.h
lib/Frontend/CompilerInvocation.cpp
lib/Lex/PPLexerChange.cpp
lib/Serialization/ASTWriter.cpp
test/Modules/macro-ambiguity.cpp
test/Modules/macro-reexport.cpp
test/Modules/macros.c
test/Modules/macros2.c

index bd90a2c2d74c1e8369040a864a09ac125f6af3c5..baabfe3a49f9bf96b97d2df70ccbcece21f8f684 100644 (file)
@@ -130,6 +130,7 @@ COMPATIBLE_LANGOPT(ModulesStrictDeclUse, 1, 0, "require declaration of module us
 LANGOPT(ModulesErrorRecovery, 1, 1, "automatically import modules as needed when performing error recovery")
 BENIGN_LANGOPT(ModulesImplicitMaps, 1, 1, "use files called module.modulemap implicitly as module maps")
 BENIGN_LANGOPT(ImplicitModules, 1, 1, "build modules that are not specified via -fmodule-file")
+COMPATIBLE_LANGOPT(ModulesLocalVisibility, 1, 0, "local submodule visibility")
 COMPATIBLE_LANGOPT(Optimize          , 1, 0, "__OPTIMIZE__ predefined macro")
 COMPATIBLE_LANGOPT(OptimizeSize      , 1, 0, "__OPTIMIZE_SIZE__ predefined macro")
 LANGOPT(Static            , 1, 0, "__STATIC__ predefined macro (as opposed to __DYNAMIC__)")
index 3c08b24d510ee1b8d7a956bfcbc3df771f7dffbd..2ce880ee7b37a285f123e5b21d650e9a1b00ebd0 100644 (file)
@@ -480,14 +480,24 @@ private:
 class VisibleModuleSet {
 public:
   VisibleModuleSet() : Generation(0) {}
+  VisibleModuleSet(VisibleModuleSet &&O)
+      : ImportLocs(std::move(O.ImportLocs)), Generation(O.Generation ? 1 : 0) {
+    O.ImportLocs.clear();
+    ++O.Generation;
+  }
 
+  /// Move from another visible modules set. Guaranteed to leave the source
+  /// empty and bump the generation on both.
   VisibleModuleSet &operator=(VisibleModuleSet &&O) {
     ImportLocs = std::move(O.ImportLocs);
+    O.ImportLocs.clear();
+    ++O.Generation;
     ++Generation;
     return *this;
   }
 
-  /// \brief Get the current visibility generation.
+  /// \brief Get the current visibility generation. Incremented each time the
+  /// set of visible modules changes in any way.
   unsigned getGeneration() const { return Generation; }
 
   /// \brief Determine whether a module is visible.
index 12b4c6557403259eb39e1fd97351d3272f488e8e..196035e2efeba19249a563476245a15a39c86af4 100644 (file)
@@ -347,6 +347,10 @@ def fmodule_map_file_home_is_cwd : Flag<["-"], "fmodule-map-file-home-is-cwd">,
 def fmodule_feature : Separate<["-"], "fmodule-feature">,
   MetaVarName<"<feature>">,
   HelpText<"Enable <feature> in module map requires declarations">;
+def fmodules_local_submodule_visibility :
+  Flag<["-"], "fmodules-local-submodule-visibility">,
+  HelpText<"Enforce name visibility rules across submodules of the same "
+           "top-level module.">;
 
 let Group = Action_Group in {
 
index 9950974aeb076e750b4b6bd5de23b37c66db129a..108fc37ac27ab0151c6be638b8a1ef60d1f9b748 100644 (file)
@@ -466,12 +466,15 @@ class Preprocessor : public RefCountedBase<Preprocessor> {
         return Info->OverriddenMacros;
       return None;
     }
-    void setOverriddenMacros(ArrayRef<ModuleMacro*> Overrides) {
+    void setOverriddenMacros(Preprocessor &PP,
+                             ArrayRef<ModuleMacro *> Overrides) {
       auto *Info = State.dyn_cast<ModuleMacroInfo*>();
       if (!Info) {
-        assert(Overrides.empty() &&
-               "have overrides but never had module macro");
-        return;
+        if (Overrides.empty())
+          return;
+        Info = new (PP.getPreprocessorAllocator())
+            ModuleMacroInfo(State.get<MacroDirective *>());
+        State = Info;
       }
       Info->OverriddenMacros.clear();
       Info->OverriddenMacros.insert(Info->OverriddenMacros.end(),
@@ -498,16 +501,11 @@ class Preprocessor : public RefCountedBase<Preprocessor> {
     Module *M;
     /// The location at which the module was included.
     SourceLocation ImportLoc;
-
-    struct SavedMacroInfo {
-      SavedMacroInfo() : Latest(nullptr) {}
-      MacroDirective *Latest;
-      llvm::TinyPtrVector<ModuleMacro*> Overridden;
-    };
     /// The macros that were visible before we entered the module.
-    llvm::DenseMap<const IdentifierInfo*, SavedMacroInfo> Macros;
+    MacroMap Macros;
+    /// The set of modules that was visible in the surrounding submodule.
+    VisibleModuleSet VisibleModules;
 
-    // FIXME: VisibleModules?
     // FIXME: CounterValue?
     // FIXME: PragmaPushMacroInfo?
   };
@@ -662,6 +660,7 @@ public:
   HeaderSearch &getHeaderSearchInfo() const { return HeaderInfo; }
 
   IdentifierTable &getIdentifierTable() { return Identifiers; }
+  const IdentifierTable &getIdentifierTable() const { return Identifiers; }
   SelectorTable &getSelectorTable() { return Selectors; }
   Builtin::Context &getBuiltinInfo() { return BuiltinInfo; }
   llvm::BumpPtrAllocator &getPreprocessorAllocator() { return BP; }
index a91e31e36c1737a650fb9b9db6909e59cafa8e2e..79f80d0df745388140d1778eda39fa4bc108f446 100644 (file)
@@ -1508,6 +1508,8 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
   Opts.ModulesStrictDeclUse = Args.hasArg(OPT_fmodules_strict_decluse);
   Opts.ModulesDeclUse =
       Args.hasArg(OPT_fmodules_decluse) || Opts.ModulesStrictDeclUse;
+  Opts.ModulesLocalVisibility =
+      Args.hasArg(OPT_fmodules_local_submodule_visibility);
   Opts.ModulesSearchAll = Opts.Modules &&
     !Args.hasArg(OPT_fno_modules_search_all) &&
     Args.hasArg(OPT_fmodules_search_all);
index 6766fdfcd07ead86b8e60f55e79a9f909daf20ac..c506aa7f6f0073f857aac5e6510a38644771f120 100644 (file)
@@ -612,16 +612,25 @@ void Preprocessor::HandleMicrosoftCommentPaste(Token &Tok) {
 void Preprocessor::EnterSubmodule(Module *M, SourceLocation ImportLoc) {
   // Save the current state for future imports.
   BuildingSubmoduleStack.push_back(BuildingSubmoduleInfo(M, ImportLoc));
-
   auto &Info = BuildingSubmoduleStack.back();
-  // Copy across our macros and start the submodule with the current state.
-  // FIXME: We should start each submodule with just the predefined macros.
-  for (auto &M : Macros) {
-    BuildingSubmoduleInfo::SavedMacroInfo SMI;
-    SMI.Latest = M.second.getLatest();
-    auto O = M.second.getOverriddenMacros();
-    SMI.Overridden.insert(SMI.Overridden.end(), O.begin(), O.end());
-    Info.Macros.insert(std::make_pair(M.first, SMI));
+  Info.Macros.swap(Macros);
+  // Save our visible modules set. This is guaranteed to clear the set.
+  if (getLangOpts().ModulesLocalVisibility)
+    Info.VisibleModules = std::move(VisibleModules);
+
+  // Determine the set of starting macros for this submodule.
+  // FIXME: If we re-enter a submodule, should we restore its MacroDirectives?
+  auto &StartingMacros = (getLangOpts().ModulesLocalVisibility &&
+                          BuildingSubmoduleStack.size() > 1)
+                             ? BuildingSubmoduleStack[0].Macros
+                             : Info.Macros;
+
+  // Restore to the starting state.
+  // FIXME: Do this lazily, when each macro name is first referenced.
+  for (auto &Macro : StartingMacros) {
+    MacroState MS(Macro.second.getLatest());
+    MS.setOverriddenMacros(*this, MS.getOverriddenMacros());
+    Macros.insert(std::make_pair(Macro.first, std::move(MS)));
   }
 }
 
@@ -631,19 +640,35 @@ void Preprocessor::LeaveSubmodule() {
   // Create ModuleMacros for any macros defined in this submodule.
   for (auto &Macro : Macros) {
     auto *II = const_cast<IdentifierInfo*>(Macro.first);
-    auto SavedInfo = Info.Macros.lookup(II);
+    auto &OuterInfo = Info.Macros[II];
+
+    // Find the starting point for the MacroDirective chain in this submodule.
+    auto *OldMD = OuterInfo.getLatest();
+    if (getLangOpts().ModulesLocalVisibility &&
+        BuildingSubmoduleStack.size() > 1) {
+      auto &PredefMacros = BuildingSubmoduleStack[0].Macros;
+      auto PredefMacroIt = PredefMacros.find(Macro.first);
+      if (PredefMacroIt == PredefMacros.end())
+        OldMD = nullptr;
+      else
+        OldMD = PredefMacroIt->second.getLatest();
+    }
 
     // This module may have exported a new macro. If so, create a ModuleMacro
     // representing that fact.
     bool ExplicitlyPublic = false;
-    for (auto *MD = Macro.second.getLatest(); MD != SavedInfo.Latest;
+    for (auto *MD = Macro.second.getLatest(); MD != OldMD;
          MD = MD->getPrevious()) {
       assert(MD && "broken macro directive chain");
 
       // Skip macros defined in other submodules we #included along the way.
-      Module *Mod = getModuleContainingLocation(MD->getLocation());
-      if (Mod != Info.M)
-        continue;
+      // There's no point doing this if we're tracking local submodule
+      // visibiltiy, since there can be no such directives in our list.
+      if (!getLangOpts().ModulesLocalVisibility) {
+        Module *Mod = getModuleContainingLocation(MD->getLocation());
+        if (Mod != Info.M)
+          continue;
+      }
 
       if (auto *VisMD = dyn_cast<VisibilityMacroDirective>(MD)) {
         // The latest visibility directive for a name in a submodule affects
@@ -667,11 +692,21 @@ void Preprocessor::LeaveSubmodule() {
       }
     }
 
-    // Restore the macro's overrides list.
-    Macro.second.setOverriddenMacros(SavedInfo.Overridden);
+    // Maintain a single macro directive chain if we're not tracking
+    // per-submodule macro visibility.
+    if (!getLangOpts().ModulesLocalVisibility)
+      OuterInfo.setLatest(Macro.second.getLatest());
   }
 
-  makeModuleVisible(Info.M, Info.ImportLoc);
+  // Put back the old macros.
+  std::swap(Info.Macros, Macros);
+
+  if (getLangOpts().ModulesLocalVisibility)
+    VisibleModules = std::move(Info.VisibleModules);
+
+  // A nested #include makes the included submodule visible.
+  if (BuildingSubmoduleStack.size() > 1)
+    makeModuleVisible(Info.M, Info.ImportLoc);
 
   BuildingSubmoduleStack.pop_back();
 }
index 8a3b9d5163ffad5dc7f567a9a561fc5af849de5a..7ea50e5003f7f0dba23bdbadea3cfe810455cfe3 100644 (file)
@@ -2019,37 +2019,23 @@ void ASTWriter::WritePreprocessor(const Preprocessor &PP, bool IsModule) {
   // Loop over all the macro directives that are live at the end of the file,
   // emitting each to the PP section.
 
-  // Construct the list of macro directives that need to be serialized.
-  typedef std::pair<const IdentifierInfo *, MacroDirective *> MacroChain;
-  SmallVector<MacroChain, 2> MacroDirectives;
-  for (Preprocessor::macro_iterator
-         I = PP.macro_begin(/*IncludeExternalMacros=*/false),
-         E = PP.macro_end(/*IncludeExternalMacros=*/false);
-       I != E; ++I) {
-    MacroDirectives.push_back(std::make_pair(I->first, I->second.getLatest()));
-  }
-
+  // Construct the list of identifiers with macro directives that need to be
+  // serialized.
+  SmallVector<const IdentifierInfo *, 128> MacroIdentifiers;
+  for (auto &Id : PP.getIdentifierTable())
+    if (Id.second->hadMacroDefinition() &&
+        (!Id.second->isFromAST() ||
+         Id.second->hasChangedSinceDeserialization()))
+      MacroIdentifiers.push_back(Id.second);
   // Sort the set of macro definitions that need to be serialized by the
   // name of the macro, to provide a stable ordering.
-  int (*Cmp)(const MacroChain*, const MacroChain*) =
-    [](const MacroChain *A, const MacroChain *B) -> int {
-      return A->first->getName().compare(B->first->getName());
-    };
-  llvm::array_pod_sort(MacroDirectives.begin(), MacroDirectives.end(), Cmp);
+  std::sort(MacroIdentifiers.begin(), MacroIdentifiers.end(),
+            llvm::less_ptr<IdentifierInfo>());
 
   // Emit the macro directives as a list and associate the offset with the
   // identifier they belong to.
-  for (auto &Chain : MacroDirectives) {
-    const IdentifierInfo *Name = Chain.first;
-    MacroDirective *MD = Chain.second;
-
-    // If the macro or identifier need no updates, don't write the macro history
-    // for this one.
-    // FIXME: Chain the macro history instead of re-writing it.
-    if (MD && MD->isFromPCH() &&
-        Name->isFromAST() && !Name->hasChangedSinceDeserialization())
-      continue;
-
+  for (const IdentifierInfo *Name : MacroIdentifiers) {
+    MacroDirective *MD = PP.getLocalMacroDirectiveHistory(Name);
     auto StartOffset = Stream.GetCurrentBitNo();
 
     // Emit the macro directives in reverse source order.
@@ -2069,6 +2055,7 @@ void ASTWriter::WritePreprocessor(const Preprocessor &PP, bool IsModule) {
     }
 
     // Write out any exported module macros.
+    bool EmittedModuleMacros = false;
     if (IsModule) {
       auto Leafs = PP.getLeafModuleMacros(Name);
       SmallVector<ModuleMacro*, 8> Worklist(Leafs.begin(), Leafs.end());
@@ -2079,7 +2066,7 @@ void ASTWriter::WritePreprocessor(const Preprocessor &PP, bool IsModule) {
         // Emit a record indicating this submodule exports this macro.
         ModuleMacroRecord.push_back(
             getSubmoduleID(Macro->getOwningModule()));
-        ModuleMacroRecord.push_back(getMacroID(Macro->getMacroInfo()));
+        ModuleMacroRecord.push_back(getMacroRef(Macro->getMacroInfo(), Name));
         for (auto *M : Macro->overrides())
           ModuleMacroRecord.push_back(getSubmoduleID(M->getOwningModule()));
 
@@ -2090,10 +2077,12 @@ void ASTWriter::WritePreprocessor(const Preprocessor &PP, bool IsModule) {
         for (auto *M : Macro->overrides())
           if (++Visits[M] == M->getNumOverridingMacros())
             Worklist.push_back(M);
+
+        EmittedModuleMacros = true;
       }
     }
 
-    if (Record.empty())
+    if (Record.empty() && !EmittedModuleMacros)
       continue;
 
     IdentMacroDirectivesOffsetMap[Name] = StartOffset;
index ea9e4f549f0d6615ab35e81a745d78271c447599..af43b35839abfff1903008fa5c5a32d961b79e8b 100644 (file)
 // RUN:   -fmodule-file=%t/c.pcm \
 // RUN:   -fmodule-file=%t/d.pcm \
 // RUN:   -Wambiguous-macro -verify macro-ambiguity.cpp
+//
+// RUN: %clang_cc1 -fmodules -x c++ -fmodules-cache-path=%t \
+// RUN:   -v -fmodules-local-submodule-visibility \
+// RUN:   -iquote Inputs/macro-ambiguity/a/quote \
+// RUN:   -isystem Inputs/macro-ambiguity/a/system \
+// RUN:   -iquote Inputs/macro-ambiguity/b/quote \
+// RUN:   -isystem Inputs/macro-ambiguity/b/system \
+// RUN:   -iquote Inputs/macro-ambiguity/c/quote \
+// RUN:   -isystem Inputs/macro-ambiguity/c/system \
+// RUN:   -iquote Inputs/macro-ambiguity/d/quote \
+// RUN:   -isystem Inputs/macro-ambiguity/d/system \
+// RUN:   -iquote Inputs/macro-ambiguity/e/quote \
+// RUN:   -isystem Inputs/macro-ambiguity/e/system \
+// RUN:   -fno-implicit-modules -fno-modules-implicit-maps \
+// RUN:   -fmodule-map-file-home-is-cwd \
+// RUN:   -fmodule-map-file=Inputs/macro-ambiguity/module.modulemap \
+// RUN:   -fmodule-file=%t/a.pcm \
+// RUN:   -fmodule-file=%t/b.pcm \
+// RUN:   -fmodule-file=%t/c.pcm \
+// RUN:   -fmodule-file=%t/d.pcm \
+// RUN:   -Wambiguous-macro -verify macro-ambiguity.cpp
 
 // Include the textual headers first to maximize the ways in which things can
 // become ambiguous.
index 1df49b948d3698c093d730530ba87c5c18498045..2be6f1532cb49e365782db80b411525633cce8ea 100644 (file)
@@ -7,6 +7,15 @@
 // RUN: %clang_cc1 -fsyntax-only -DD2 -I%S/Inputs/macro-reexport -fmodules %s -fmodules-cache-path=%t -verify
 // RUN: %clang_cc1 -fsyntax-only -DF1 -I%S/Inputs/macro-reexport %s -fmodules-cache-path=%t -verify
 // RUN: %clang_cc1 -fsyntax-only -DF1 -I%S/Inputs/macro-reexport -fmodules %s -fmodules-cache-path=%t -verify
+//
+// RUN: %clang_cc1 -fmodules-local-submodule-visibility -fsyntax-only -DC1 -I%S/Inputs/macro-reexport %s -fmodules-cache-path=%t -verify
+// RUN: %clang_cc1 -fmodules-local-submodule-visibility -fsyntax-only -DC1 -I%S/Inputs/macro-reexport -fmodules %s -fmodules-cache-path=%t -verify
+// RUN: %clang_cc1 -fmodules-local-submodule-visibility -fsyntax-only -DD1 -I%S/Inputs/macro-reexport %s -fmodules-cache-path=%t -verify
+// RUN: %clang_cc1 -fmodules-local-submodule-visibility -fsyntax-only -DD1 -I%S/Inputs/macro-reexport -fmodules %s -fmodules-cache-path=%t -verify
+// RUN: %clang_cc1 -fmodules-local-submodule-visibility -fsyntax-only -DD2 -I%S/Inputs/macro-reexport %s -fmodules-cache-path=%t -verify
+// RUN: %clang_cc1 -fmodules-local-submodule-visibility -fsyntax-only -DD2 -I%S/Inputs/macro-reexport -fmodules %s -fmodules-cache-path=%t -verify
+// RUN: %clang_cc1 -fmodules-local-submodule-visibility -fsyntax-only -DF1 -I%S/Inputs/macro-reexport %s -fmodules-cache-path=%t -verify
+// RUN: %clang_cc1 -fmodules-local-submodule-visibility -fsyntax-only -DF1 -I%S/Inputs/macro-reexport -fmodules %s -fmodules-cache-path=%t -verify
 
 #if defined(F1)
 #include "f1.h"
index e367fb685b95e25ced023cafd043948baa9c8ed3..076166966b73a2eda6fce26f748aaeb078aa75a2 100644 (file)
@@ -1,9 +1,6 @@
 // RUN: rm -rf %t
-// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=macros_top %S/Inputs/module.map
-// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=macros_left %S/Inputs/module.map
-// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=macros_right %S/Inputs/module.map
-// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=macros %S/Inputs/module.map
 // RUN: %clang_cc1 -fmodules -x objective-c -verify -fmodules-cache-path=%t -I %S/Inputs %s
+// RUN: %clang_cc1 -fmodules -DLOCAL_VISIBILITY -fmodules-local-submodule-visibility -x objective-c -verify -fmodules-cache-path=%t -I %S/Inputs %s
 // RUN: not %clang_cc1 -E -fmodules -x objective-c -fmodules-cache-path=%t -I %S/Inputs %s | FileCheck -check-prefix CHECK-PREPROCESSED %s
 // FIXME: When we have a syntax for modules in C, use that.
 // These notes come from headers in modules, and are bogus.
@@ -143,11 +140,20 @@ TOP_DEF_RIGHT_UNDEF *TDRUf() { return TDRUp; }
 
 int TOP_DEF_RIGHT_UNDEF; // ok, no longer defined
 
-// FIXME: When macros_right.undef is built, macros_top is visible because
-// the state from building macros_right leaks through, so macros_right.undef
-// undefines macros_top's macro.
-#ifdef TOP_RIGHT_UNDEF
-# error TOP_RIGHT_UNDEF should not be defined
+#ifdef LOCAL_VISIBILITY
+// TOP_RIGHT_UNDEF should not be undefined, because macros_right.undef does
+// not undefine macros_right's macro.
+# ifndef TOP_RIGHT_UNDEF
+#  error TOP_RIGHT_UNDEF should still be defined
+# endif
+#else
+// When macros_right.undef is built and local submodule visibility is not
+// enabled, macros_top is visible because the state from building
+// macros_right leaks through, so macros_right.undef undefines macros_top's
+// macro.
+# ifdef TOP_RIGHT_UNDEF
+#  error TOP_RIGHT_UNDEF should not be defined
+# endif
 #endif
 
 @import macros_other;
index c4c8059011ccd18787b4097250a4a3cbc91094a3..addb9b495cf8b76ed26238117fdd252c2822a928 100644 (file)
@@ -1,9 +1,6 @@
 // RUN: rm -rf %t
-// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=macros_top %S/Inputs/module.map
-// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=macros_left %S/Inputs/module.map
-// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=macros_right %S/Inputs/module.map
-// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=macros %S/Inputs/module.map
 // RUN: %clang_cc1 -fmodules -x objective-c -verify -fmodules-cache-path=%t -I %S/Inputs %s
+// RUN: %clang_cc1 -fmodules -fmodules-local-submodule-visibility -x objective-c -verify -fmodules-cache-path=%t -I %S/Inputs %s -DLOCAL_VISIBILITY
 
 // This test checks some of the same things as macros.c, but imports modules in
 // a different order.
@@ -49,9 +46,15 @@ void test() {
 
 @import macros_right.undef;
 
-// FIXME: See macros.c.
-#ifdef TOP_RIGHT_UNDEF
-# error TOP_RIGHT_UNDEF should not be defined
+// See macros.c.
+#ifdef LOCAL_VISIBILITY
+# ifndef TOP_RIGHT_UNDEF
+#  error TOP_RIGHT_UNDEF should still be defined
+# endif
+#else
+# ifdef TOP_RIGHT_UNDEF
+#  error TOP_RIGHT_UNDEF should not be defined
+# endif
 #endif
 
 #ifndef TOP_OTHER_UNDEF1