vsapsai created this revision.
Herald added a subscriber: ributzka.
vsapsai requested review of this revision.
Herald added a project: clang.

The test case is for Objective-C but it is likely other languages like
C++ are affected as well, just haven't created appropriate test cases.

The test case fails with the following error

  clang/test/Modules/Output/ambiguous-enum-lookup.m.tmp/test.m:7:11: error: 
reference to 'kEnumValue' is ambiguous
      s.x = kEnumValue;
            ^
  
clang/test/Modules/Output/ambiguous-enum-lookup.m.tmp/Frameworks/NonModular.framework/Headers/NonModular.h:8:5:
 note: candidate found by name lookup is 'kEnumValue'
      kEnumValue = 1,
      ^
  
clang/test/Modules/Output/ambiguous-enum-lookup.m.tmp/Frameworks/NonModular.framework/Headers/NonModular.h:8:5:
 note: candidate found by name lookup is 'kEnumValue'
      kEnumValue = 1,
      ^

It happens because we have 2 EnumConstantDecl for 'kEnumValue' - one from
PiecewiseVisible module, another non-modular. And the the name lookup is
ambiguous because these decls have different canonical decls.
Non-modular decl believes it is the canonical decl because that's the
behavior for Mergeable and in the source code non-modular decl comes
before other EnumConstantDecl. Modular EnumConstantDecl has no intention
of being canonical decl but it is deserialized *before* non-modular decl
is created, so as the only decl it becomes the canonical decl.

The idea for the fix is to skip deserialization of decls from hidden
(sub)modules, thus allowing to establish correct canonical decl. This is
a proof of concept, the implementation isn't optimal, and there are
failing tests. I mostly wanted to share the approach to discuss it.

Another high-level approach I've considered is to enhance handling decls
from hidden modules and to delay connecting them with redecl chains and
canonical decls till the moment they become visible. I've decided to go
with "avoid deserialization" approach because this way we should be
doing less work and it seems less error-prone compared to juggling with
existing decls.

rdar://82908206


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D114411

Files:
  clang/lib/Serialization/ASTReader.cpp
  clang/lib/Serialization/ASTReaderDecl.cpp
  clang/lib/Serialization/ASTWriterDecl.cpp
  clang/test/Modules/ambiguous-enum-lookup.m

Index: clang/test/Modules/ambiguous-enum-lookup.m
===================================================================
--- /dev/null
+++ clang/test/Modules/ambiguous-enum-lookup.m
@@ -0,0 +1,42 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: %clang_cc1 -fsyntax-only -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache -F %t/Frameworks %t/test.m
+
+//--- Frameworks/NonModular.framework/Headers/NonModular.h
+#ifndef NonModular_NonModular_h
+#define NonModular_NonModular_h
+struct SomeStruct {
+    int x;
+};
+
+enum SomeEnum {
+    kEnumValue = 1,
+};
+#endif
+
+//--- Frameworks/PiecewiseVisible.framework/Headers/InitiallyVisible.h
+// empty
+
+//--- Frameworks/PiecewiseVisible.framework/Headers/InitiallyHidden.h
+#include <NonModular/NonModular.h>
+
+//--- Frameworks/PiecewiseVisible.framework/Modules/module.modulemap
+framework module PiecewiseVisible {
+  header "InitiallyVisible.h"
+  export *
+
+  explicit module HiddenPiece {
+    header "InitiallyHidden.h"
+    export *
+  }
+}
+
+//--- test.m
+#include <PiecewiseVisible/InitiallyVisible.h>
+#include <NonModular/NonModular.h>
+#include <PiecewiseVisible/InitiallyHidden.h>
+
+void test() {
+    struct SomeStruct s;
+    s.x = kEnumValue;
+}
Index: clang/lib/Serialization/ASTWriterDecl.cpp
===================================================================
--- clang/lib/Serialization/ASTWriterDecl.cpp
+++ clang/lib/Serialization/ASTWriterDecl.cpp
@@ -269,6 +269,7 @@
 }
 
 void ASTDeclWriter::Visit(Decl *D) {
+  Record.push_back(Writer.getSubmoduleID(D->getOwningModule()));
   DeclVisitor<ASTDeclWriter>::Visit(D);
 
   // Source locations require array (variable-length) abbreviations.  The
@@ -1911,6 +1912,7 @@
   // Abbreviation for DECL_FIELD
   Abv = std::make_shared<BitCodeAbbrev>();
   Abv->Add(BitCodeAbbrevOp(serialization::DECL_FIELD));
+  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID
   // Decl
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // DeclContext
   Abv->Add(BitCodeAbbrevOp(0));                       // LexicalDeclContext
@@ -1944,6 +1946,7 @@
   // Abbreviation for DECL_OBJC_IVAR
   Abv = std::make_shared<BitCodeAbbrev>();
   Abv->Add(BitCodeAbbrevOp(serialization::DECL_OBJC_IVAR));
+  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID
   // Decl
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // DeclContext
   Abv->Add(BitCodeAbbrevOp(0));                       // LexicalDeclContext
@@ -1980,6 +1983,7 @@
   // Abbreviation for DECL_ENUM
   Abv = std::make_shared<BitCodeAbbrev>();
   Abv->Add(BitCodeAbbrevOp(serialization::DECL_ENUM));
+  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID
   // Redeclarable
   Abv->Add(BitCodeAbbrevOp(0));                       // No redeclaration
   // Decl
@@ -2030,6 +2034,7 @@
   // Abbreviation for DECL_RECORD
   Abv = std::make_shared<BitCodeAbbrev>();
   Abv->Add(BitCodeAbbrevOp(serialization::DECL_RECORD));
+  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID
   // Redeclarable
   Abv->Add(BitCodeAbbrevOp(0));                       // No redeclaration
   // Decl
@@ -2092,6 +2097,7 @@
   // Abbreviation for DECL_PARM_VAR
   Abv = std::make_shared<BitCodeAbbrev>();
   Abv->Add(BitCodeAbbrevOp(serialization::DECL_PARM_VAR));
+  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID
   // Redeclarable
   Abv->Add(BitCodeAbbrevOp(0));                       // No redeclaration
   // Decl
@@ -2140,6 +2146,7 @@
   // Abbreviation for DECL_TYPEDEF
   Abv = std::make_shared<BitCodeAbbrev>();
   Abv->Add(BitCodeAbbrevOp(serialization::DECL_TYPEDEF));
+  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID
   // Redeclarable
   Abv->Add(BitCodeAbbrevOp(0));                       // No redeclaration
   // Decl
@@ -2169,6 +2176,7 @@
   // Abbreviation for DECL_VAR
   Abv = std::make_shared<BitCodeAbbrev>();
   Abv->Add(BitCodeAbbrevOp(serialization::DECL_VAR));
+  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID
   // Redeclarable
   Abv->Add(BitCodeAbbrevOp(0));                       // No redeclaration
   // Decl
@@ -2221,6 +2229,7 @@
   // Abbreviation for DECL_CXX_METHOD
   Abv = std::make_shared<BitCodeAbbrev>();
   Abv->Add(BitCodeAbbrevOp(serialization::DECL_CXX_METHOD));
+  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // SubmoduleID
   // RedeclarableDecl
   Abv->Add(BitCodeAbbrevOp(0));                         // CanonicalDecl
   // Decl
Index: clang/lib/Serialization/ASTReaderDecl.cpp
===================================================================
--- clang/lib/Serialization/ASTReaderDecl.cpp
+++ clang/lib/Serialization/ASTReaderDecl.cpp
@@ -3870,6 +3870,15 @@
     llvm::report_fatal_error(
         Twine("ASTReader::readDeclRecord failed reading decl code: ") +
         toString(MaybeDeclCode.takeError()));
+  if (unsigned DeclSubmoduleID = getGlobalSubmoduleID(*Loc.F, Record.readInt())) {
+    if (SemaObj) {
+      Module *DeclOwner = getSubmodule(DeclSubmoduleID);
+      if (DeclOwner && !SemaObj->isModuleVisible(DeclOwner)) {
+        // Skip deserialization of Decls from a hidden module.
+        return nullptr;
+      }
+    }
+  }
   switch ((DeclCode)MaybeDeclCode.get()) {
   case DECL_CONTEXT_LEXICAL:
   case DECL_CONTEXT_VISIBLE:
Index: clang/lib/Serialization/ASTReader.cpp
===================================================================
--- clang/lib/Serialization/ASTReader.cpp
+++ clang/lib/Serialization/ASTReader.cpp
@@ -8474,18 +8474,20 @@
       continue;
     }
 
-    NamedDecl *D = cast<NamedDecl>(GetDecl(DeclIDs[I]));
+    if (Decl *DeserializedD = GetDecl(DeclIDs[I])) {
+      NamedDecl *D = cast<NamedDecl>(DeserializedD);
 
-    // If we're simply supposed to record the declarations, do so now.
-    if (Decls) {
-      Decls->push_back(D);
-      continue;
-    }
+      // If we're simply supposed to record the declarations, do so now.
+      if (Decls) {
+        Decls->push_back(D);
+        continue;
+      }
 
-    // Introduce this declaration into the translation-unit scope
-    // and add it to the declaration chain for this identifier, so
-    // that (unqualified) name lookup will find it.
-    pushExternalDeclIntoScope(D, II);
+      // Introduce this declaration into the translation-unit scope
+      // and add it to the declaration chain for this identifier, so
+      // that (unqualified) name lookup will find it.
+      pushExternalDeclIntoScope(D, II);
+    }
   }
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to