ChuanqiXu created this revision.
ChuanqiXu added reviewers: rsmith, aaron.ballman, urnathan, 
hubert.reinterpretcast, erichkeane.
ChuanqiXu added a project: clang.
Herald added a subscriber: dexonsmith.
ChuanqiXu requested review of this revision.
Herald added a subscriber: cfe-commits.

This file intends to support: https://eel.is/c++draft/module.unit#3.

Module partition should be a special module interface which is imported by 
module units in the same module.
Note that although there is implementation module partition, it should be a 
module interface too since it could be imported by module units in the same 
module.

For a module partition `primary.module.name:partition.name`, the user should 
generate a `pcm` file with name `primary.module.name-partition.name.pcm`. 
Simply replace `:` with `-`. And when we try to import a partition with 
`partition-name` in `primary.module.name`, the compiler would try to search for 
`primary.module.name-partition.name.pcm` in the given path. The strategy to 
replace `:` with `-` keeps consistency with GCC.

The key problem I see in introducing module partitions is that the judgement 
for modules. Before, there would be only module interface unit for each module. 
But now, it would be many partitions which are belongs in the same module. The 
judgement of modules matter when the compiler want to decide whether or not a 
declaration is visible or reachable.

And my solution is to compare the prefix of the name before '-' to judge 
whether or not the two module belongs to the same module. Since '-' shouldn't 
show up in the original module name by the definition.
For example, 'X-A' and `X-B` are in the same module. But `X-A` and `Y-A` are 
not.

BTW, it would be problem if we want to import a module partition in another 
module by renaming. See 
`clang/test/CXX/module/module.unit/p3/FromWrongModule.cpp` for example.

The method looks simple and available in simple demos. I want to hear more 
opinions.

Test Plan: check-all


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D113972

Files:
  clang/include/clang/Basic/DiagnosticParseKinds.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Basic/Module.h
  clang/include/clang/Sema/Sema.h
  clang/lib/Parse/Parser.cpp
  clang/lib/Sema/SemaDecl.cpp
  clang/lib/Sema/SemaLookup.cpp
  clang/lib/Sema/SemaModule.cpp
  clang/lib/Sema/SemaType.cpp
  clang/test/CXX/module/module.global.frag/Inputs/X-A.cppm
  clang/test/CXX/module/module.global.frag/Inputs/header.h
  clang/test/CXX/module/module.global.frag/p2.cpp
  clang/test/CXX/module/module.import/Inputs/M-Part.cppm
  clang/test/CXX/module/module.import/p8.cpp
  clang/test/CXX/module/module.private.frag/p1.cpp
  clang/test/CXX/module/module.reach/Inputs/p4/M-A.cppm
  clang/test/CXX/module/module.reach/Inputs/p4/M-B.cppm
  clang/test/CXX/module/module.reach/Inputs/p4/M.cppm
  clang/test/CXX/module/module.reach/p4/M-C.cppm
  clang/test/CXX/module/module.reach/p4/X.cpp
  clang/test/CXX/module/module.unit/Inputs/p3/X.cppm
  clang/test/CXX/module/module.unit/Inputs/p3/parta.cppm
  clang/test/CXX/module/module.unit/Inputs/p3/partb.cppm
  clang/test/CXX/module/module.unit/Inputs/p4/A-Foo.cppm
  clang/test/CXX/module/module.unit/Inputs/p4/A-Impl.cpp
  clang/test/CXX/module/module.unit/Inputs/p4/A-Internals.cppm
  clang/test/CXX/module/module.unit/Inputs/p4/A.cppm
  clang/test/CXX/module/module.unit/Inputs/p8/B-X1.cppm
  clang/test/CXX/module/module.unit/Inputs/p8/B-X2.cppm
  clang/test/CXX/module/module.unit/Inputs/p8/B-Y.cppm
  clang/test/CXX/module/module.unit/Inputs/p8/B.cppm
  clang/test/CXX/module/module.unit/p3.cpp
  clang/test/CXX/module/module.unit/p3/FromNonModule.cpp
  clang/test/CXX/module/module.unit/p3/FromWrongModule.cpp
  clang/test/CXX/module/module.unit/p3/simple.cpp
  clang/test/CXX/module/module.unit/p3/user.cpp
  clang/test/CXX/module/module.unit/p4/user.cpp
  clang/test/CXX/module/module.unit/p8.cpp
  clang/test/CXX/module/module.unit/p8/B-Impl.cppm
  clang/test/CXX/module/module.unit/p8/ImplicitImport.cpp

Index: clang/test/CXX/module/module.unit/p8/ImplicitImport.cpp
===================================================================
--- clang/test/CXX/module/module.unit/p8/ImplicitImport.cpp
+++ clang/test/CXX/module/module.unit/p8/ImplicitImport.cpp
@@ -24,12 +24,10 @@
 export module bar;
 
 #elif MODE == 4
-module foo:bar; // expected-error {{not yet supported}}
-#define IMPORTED // FIXME
+module foo:bar;
 
 #elif MODE == 5
-export module foo:bar; // expected-error {{not yet supported}} expected-error {{redefinition}} expected-note@* {{loaded from}}
-#define IMPORTED // FIXME
+export module foo:bar; 
 
 #endif
 
Index: clang/test/CXX/module/module.unit/p8/B-Impl.cppm
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.unit/p8/B-Impl.cppm
@@ -0,0 +1,12 @@
+// RUN: rm -fr %t
+// RUN: mkdir %t
+// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p8/B-Y.cppm -fprebuilt-module-path=%t -DTESTING -verify
+// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p8/B-Y.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/B-Y.pcm
+// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p8/B.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/B.pcm
+// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p8/B-X1.cppm -fprebuilt-module-path=%t -verify
+// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p8/B-X2.cppm -fprebuilt-module-path=%t -verify
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -fmodule-file=%t/B.pcm -verify %s
+// expected-no-diagnostics
+
+module B;                       // implicitly imports B
+int &c = n;                     // OK
Index: clang/test/CXX/module/module.unit/p4/user.cpp
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.unit/p4/user.cpp
@@ -0,0 +1,19 @@
+// RUN: rm -fr %t
+// RUN: mkdir %t
+// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p4/A-Internals.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/A-Internals.pcm
+// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p4/A-Foo.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/A-Foo.pcm
+// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p4/A.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p4/A-Impl.cpp -fmodule-file=%t/A.pcm -fprebuilt-module-path=%t -verify
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -verify %s
+
+import A;
+
+int user() {
+    return foo()+baz();
+}
+
+int user2() {
+    return foo() + baz() + bar(); // expected-error {{use of undeclared identifier 'bar'}}
+                                  // expected-note@*{{declared here}}
+                                  // NOTE: The note above notes the possible compiling error, which is irrevelant with module
+}
Index: clang/test/CXX/module/module.unit/p3/user.cpp
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.unit/p3/user.cpp
@@ -0,0 +1,13 @@
+// RUN: rm -fr %t
+// RUN: mkdir %t
+// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p3/parta.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/X-parta.pcm
+// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p3/partb.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/X-partb.pcm
+// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p3/X.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/X.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -verify %s
+// expected-no-diagnostics
+
+import X;
+
+int user() {
+    return use();
+}
Index: clang/test/CXX/module/module.unit/p3/simple.cpp
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.unit/p3/simple.cpp
@@ -0,0 +1,4 @@
+// RUN: %clang_cc1 -std=c++20 -verify %s
+// expected-no-diagnostics
+
+export module foo:bar;
Index: clang/test/CXX/module/module.unit/p3/FromWrongModule.cpp
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.unit/p3/FromWrongModule.cpp
@@ -0,0 +1,11 @@
+// RUN: rm -fr %t
+// RUN: mkdir %t
+// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p3/parta.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/Y-parta.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -verify %s
+
+export module Y;
+import :parta;  // expected-error {{error in loading module 'Y-parta' from prebuilt module path}}
+
+int use() {
+    return a();
+}
Index: clang/test/CXX/module/module.unit/p3/FromNonModule.cpp
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.unit/p3/FromNonModule.cpp
@@ -0,0 +1,11 @@
+// RUN: rm -fr %t
+// RUN: mkdir %t
+// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p3/parta.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/X-parta.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -verify %s
+
+import :parta; // expected-error {{import module partition isn't allowed in non module.}}
+               // expected-error@-1 {{not found}}
+
+int use() {
+    return a();
+}
Index: clang/test/CXX/module/module.unit/p3.cpp
===================================================================
--- clang/test/CXX/module/module.unit/p3.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-// RUN: %clang_cc1 -std=c++2a -verify %s
-
-export module foo:bar; // expected-error {{sorry, module partitions are not yet supported}}
-import :baz; // expected-error {{sorry, module partitions are not yet supported}}
Index: clang/test/CXX/module/module.unit/Inputs/p8/B.cppm
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.unit/Inputs/p8/B.cppm
@@ -0,0 +1,3 @@
+export module B;
+import :Y;                      // OK, does not create interface dependency cycle
+int n = y();
Index: clang/test/CXX/module/module.unit/Inputs/p8/B-Y.cppm
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.unit/Inputs/p8/B-Y.cppm
@@ -0,0 +1,5 @@
+module B:Y;                     // does not implicitly import B
+int y();
+#ifdef TESTING
+int _ = n;                      // expected-error {{use of undeclared identifier}}
+#endif
Index: clang/test/CXX/module/module.unit/Inputs/p8/B-X2.cppm
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.unit/Inputs/p8/B-X2.cppm
@@ -0,0 +1,4 @@
+// expected-no-diagnostics
+module B:X2;                    // does not implicitly import B
+import B;
+int &b = n;                     // OK
Index: clang/test/CXX/module/module.unit/Inputs/p8/B-X1.cppm
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.unit/Inputs/p8/B-X1.cppm
@@ -0,0 +1,2 @@
+module B:X1;                    // does not implicitly import B
+int &a = n;                     // expected-error {{use of undeclared identifier 'n'}}
Index: clang/test/CXX/module/module.unit/Inputs/p4/A.cppm
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.unit/Inputs/p4/A.cppm
@@ -0,0 +1,3 @@
+export module A;
+export import :Foo;
+export int baz();
Index: clang/test/CXX/module/module.unit/Inputs/p4/A-Internals.cppm
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.unit/Inputs/p4/A-Internals.cppm
@@ -0,0 +1,2 @@
+module A:Internals;
+int bar();
Index: clang/test/CXX/module/module.unit/Inputs/p4/A-Impl.cpp
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.unit/Inputs/p4/A-Impl.cpp
@@ -0,0 +1,5 @@
+// expected-no-diagnostics
+module A;
+import :Internals;
+int bar() { return baz() - 10; }
+int baz() { return 30; }
Index: clang/test/CXX/module/module.unit/Inputs/p4/A-Foo.cppm
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.unit/Inputs/p4/A-Foo.cppm
@@ -0,0 +1,3 @@
+export module A:Foo;
+import :Internals;
+export int foo() { return 2 * (bar() + 1); }
Index: clang/test/CXX/module/module.unit/Inputs/p3/partb.cppm
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.unit/Inputs/p3/partb.cppm
@@ -0,0 +1,5 @@
+module X:partb;
+import :parta;
+int b() {
+    return 2 + a();
+}
Index: clang/test/CXX/module/module.unit/Inputs/p3/parta.cppm
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.unit/Inputs/p3/parta.cppm
@@ -0,0 +1,5 @@
+export module X:parta;
+
+export int a() {
+    return 1;
+}
Index: clang/test/CXX/module/module.unit/Inputs/p3/X.cppm
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.unit/Inputs/p3/X.cppm
@@ -0,0 +1,6 @@
+export module X;
+export import :parta;
+import :partb;
+export int use() {
+    return a() + b();
+}
Index: clang/test/CXX/module/module.reach/p4/X.cpp
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.reach/p4/X.cpp
@@ -0,0 +1,15 @@
+// RUN: rm -fr %t
+// RUN: mkdir %t
+// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p4/M-A.cppm -emit-module-interface -o %t/M-A.pcm
+// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p4/M-B.cppm -emit-module-interface -o %t/M-B.pcm
+// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p4/M.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/M.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -verify %s
+
+export module X;
+import M;
+                                // FIXME: Eliminate the duplicated diagnostics.
+B b3;                           // expected-error {{definition of 'B' must be imported}}
+                                // expected-error@-1 {{definition of 'B' must be imported}}
+                                // expected-note@../Inputs/p4/M-B.cppm:2 {{definition here is not reachable}}
+                                // expected-note@../Inputs/p4/M-B.cppm:2 {{definition here is not reachable}}
+void g() { f(); }               // FIXME: The default argument of f() shouldn't be reachable from here. 
Index: clang/test/CXX/module/module.reach/p4/M-C.cppm
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.reach/p4/M-C.cppm
@@ -0,0 +1,10 @@
+// RUN: rm -fr %t
+// RUN: mkdir %t
+// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p4/M-A.cppm -emit-module-interface -o %t/M-A.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -verify %s
+
+module M:C;
+import :A;                      // Since the compiler couldn't see the defintion of 'B' too, it couldn't
+                                // give better diagnostic message.
+B b1;                           // expected-error {{variable has incomplete type 'B'}}
+                                // expected-note@../Inputs/p4/M-A.cppm:2 {{forward declaration of 'B'}}
Index: clang/test/CXX/module/module.reach/Inputs/p4/M.cppm
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.reach/Inputs/p4/M.cppm
@@ -0,0 +1,5 @@
+export module M;
+export import :A;
+import :B;
+B b2;
+export void f(B b = B());
Index: clang/test/CXX/module/module.reach/Inputs/p4/M-B.cppm
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.reach/Inputs/p4/M-B.cppm
@@ -0,0 +1,4 @@
+module M:B;
+struct B {
+  operator int();
+};
Index: clang/test/CXX/module/module.reach/Inputs/p4/M-A.cppm
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.reach/Inputs/p4/M-A.cppm
@@ -0,0 +1,2 @@
+export module M:A;
+export struct B;
Index: clang/test/CXX/module/module.private.frag/p1.cpp
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.private.frag/p1.cpp
@@ -0,0 +1,6 @@
+// RUN: %clang_cc1 -std=c++20 -verify %s
+
+export module X:A;
+export struct S;
+module :private; // expected-error {{private module fragment is not allowed to appear in a module partition}}
+struct S{};
Index: clang/test/CXX/module/module.import/p8.cpp
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.import/p8.cpp
@@ -0,0 +1,9 @@
+// RUN: rm -fr %t
+// RUN: mkdir %t
+// RUN: %clang_cc1 -std=c++20 %S/Inputs/M-Part.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/M-Part.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -verify %s
+// expected-no-diagnostics
+// FIXME: We couldn't export module M:Part since it is an implementation module.
+
+export module M;
+export import :Part;    // error: exported partition :Part is an implementation unit
Index: clang/test/CXX/module/module.import/Inputs/M-Part.cppm
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.import/Inputs/M-Part.cppm
@@ -0,0 +1 @@
+module M:Part;
Index: clang/test/CXX/module/module.global.frag/p2.cpp
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.global.frag/p2.cpp
@@ -0,0 +1,13 @@
+// This test tests that the declaration in the global module fragment 
+// couldn't be used in other module unit even they are in the same module.
+// RUN: rm -fr %t
+// RUN: mkdir %t
+// RUN: %clang_cc1 -std=c++20 %S/Inputs/X-A.cppm -emit-module-interface -o %t/X-A.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -verify %s
+
+export module X:B;
+import :A;
+export int foo() {
+    return n; // expected-error {{'n' must be declared before it is used}}
+              // expected-note@Inputs/header.h:1 {{declaration here is not visible}}
+}
Index: clang/test/CXX/module/module.global.frag/Inputs/header.h
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.global.frag/Inputs/header.h
@@ -0,0 +1 @@
+constexpr int n = 1;
Index: clang/test/CXX/module/module.global.frag/Inputs/X-A.cppm
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.global.frag/Inputs/X-A.cppm
@@ -0,0 +1,3 @@
+module;
+#include "header.h"
+export module X:A;
Index: clang/lib/Sema/SemaType.cpp
===================================================================
--- clang/lib/Sema/SemaType.cpp
+++ clang/lib/Sema/SemaType.cpp
@@ -8654,6 +8654,8 @@
       if (Diagnoser && SuggestedDef)
         diagnoseMissingImport(Loc, SuggestedDef, MissingImportKind::Definition,
                               /*Recover*/TreatAsComplete);
+      // FIXME: It is possible to emit duplicated diagnostic message. See
+      // CXX/module/module.reach/p4/X.cpp for example.
       return !TreatAsComplete;
     } else if (Def && !TemplateInstCallbacks.empty()) {
       CodeSynthesisContext TempInst;
Index: clang/lib/Sema/SemaModule.cpp
===================================================================
--- clang/lib/Sema/SemaModule.cpp
+++ clang/lib/Sema/SemaModule.cpp
@@ -89,7 +89,8 @@
 
 Sema::DeclGroupPtrTy
 Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
-                      ModuleDeclKind MDK, ModuleIdPath Path, bool IsFirstDecl) {
+                      ModuleDeclKind MDK, ModuleIdPath Path,
+                      ModuleIdPath PartitionPath, bool IsFirstDecl) {
   assert((getLangOpts().ModulesTS || getLangOpts().CPlusPlusModules) &&
          "should only have module decl in Modules TS or C++20");
 
@@ -165,6 +166,16 @@
     ModuleName += Piece.first->getName();
   }
 
+  if (MDK == ModuleDeclKind::Partition) {
+    std::string PartitionName;
+    for (auto &Piece : PartitionPath) {
+      if (!PartitionName.empty())
+        PartitionName += ".";
+      PartitionName += Piece.first->getName();
+    }
+    ModuleName += "-" + PartitionName;
+  }
+
   // If a module name was explicitly specified on the command line, it must be
   // correct.
   if (!getLangOpts().CurrentModule.empty() &&
@@ -180,6 +191,7 @@
   Module *Mod;
 
   switch (MDK) {
+  case ModuleDeclKind::Partition:
   case ModuleDeclKind::Interface: {
     // We can't have parsed or imported a definition of this module or parsed a
     // module map defining it already.
@@ -197,6 +209,7 @@
     // Create a Module for the module that we're defining.
     Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName,
                                            GlobalModuleFragment);
+
     assert(Mod && "module creation should not fail");
     break;
   }
@@ -272,6 +285,11 @@
     return nullptr;
   }
 
+  if (IsCurrentModulePartition()) {
+    Diag(PrivateLoc, diag::err_private_module_fragment_in_module_partition);
+    return nullptr;
+  }
+
   // FIXME: Check this isn't a module interface partition.
   // FIXME: Check that this translation unit does not import any partitions;
   // such imports would violate [basic.link]/2's "shall be the only module unit"
@@ -306,17 +324,25 @@
 
 DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
                                    SourceLocation ExportLoc,
-                                   SourceLocation ImportLoc,
-                                   ModuleIdPath Path) {
+                                   SourceLocation ImportLoc, ModuleIdPath Path,
+                                   bool IsPartition) {
   // Flatten the module path for a Modules TS module name.
   std::pair<IdentifierInfo *, SourceLocation> ModuleNameLoc;
-  if (getLangOpts().ModulesTS) {
+  if (getLangOpts().ModulesTS || IsPartition) {
     std::string ModuleName;
     for (auto &Piece : Path) {
       if (!ModuleName.empty())
         ModuleName += ".";
       ModuleName += Piece.first->getName();
     }
+
+    if (IsPartition) {
+      assert(getLangOpts().CPlusPlusModules &&
+             "Partitions is only allowed in C++20 modules.");
+      if (getLangOpts().CurrentModule.empty())
+        Diag(ImportLoc, diag::err_module_import_in_non_module);
+      ModuleName = std::string(getCurrentModuleName()) + "-" + ModuleName;
+    }
     ModuleNameLoc = {PP.getIdentifierInfo(ModuleName), Path[0].second};
     Path = ModuleIdPath(ModuleNameLoc);
   }
@@ -327,7 +353,8 @@
   if (!Mod)
     return true;
 
-  return ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Mod, Path);
+  return ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Mod, Path,
+                           IsPartition);
 }
 
 /// Determine whether \p D is lexically within an export-declaration.
@@ -340,8 +367,8 @@
 
 DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
                                    SourceLocation ExportLoc,
-                                   SourceLocation ImportLoc,
-                                   Module *Mod, ModuleIdPath Path) {
+                                   SourceLocation ImportLoc, Module *Mod,
+                                   ModuleIdPath Path, bool IsPartition) {
   VisibleModules.setVisible(Mod, ImportLoc);
 
   checkModuleImportContext(*this, Mod, ImportLoc, CurContext);
@@ -351,14 +378,13 @@
   // silently ignoring the import.
   // Import-from-implementation is valid in the Modules TS. FIXME: Should we
   // warn on a redundant import of the current module?
-  // FIXME: Import of a module from an implementation partition of the same
-  // module is permitted.
-  if (Mod->getTopLevelModuleName() == getLangOpts().CurrentModule &&
-      (getLangOpts().isCompilingModule() || !getLangOpts().ModulesTS)) {
+  if (Mod->getTopLevelPrimaryModuleName() == getCurrentModuleName() &&
+      (getLangOpts().isCompilingModule() || !getLangOpts().ModulesTS) &&
+      !IsPartition && !IsCurrentModulePartition()) {
     Diag(ImportLoc, getLangOpts().isCompilingModule()
                         ? diag::err_module_self_import
                         : diag::err_module_import_in_implementation)
-        << Mod->getFullModuleName() << getLangOpts().CurrentModule;
+        << Mod->getFullModuleName() << getCurrentModuleName();
   }
 
   SmallVector<SourceLocation, 2> IdentifierLocs;
@@ -708,3 +734,13 @@
 
   return D;
 }
+
+StringRef Sema::getCurrentModuleName() const {
+  if (!getLangOpts().CPlusPlusModules)
+    return getLangOpts().CurrentModule;
+  return StringRef(getLangOpts().CurrentModule).split('-').first;
+}
+
+bool Sema::IsCurrentModulePartition() const {
+  return StringRef(getLangOpts().CurrentModule).contains('-');
+}
Index: clang/lib/Sema/SemaLookup.cpp
===================================================================
--- clang/lib/Sema/SemaLookup.cpp
+++ clang/lib/Sema/SemaLookup.cpp
@@ -1558,10 +1558,10 @@
 
 /// Determine whether the module M is part of the current module from the
 /// perspective of a module-private visibility check.
-static bool isInCurrentModule(const Module *M, const LangOptions &LangOpts) {
+static bool isInCurrentModule(const Module *M, const Sema &SemaRef) {
   // If M is the global module fragment of a module that we've not yet finished
   // parsing, then it must be part of the current module.
-  return M->getTopLevelModuleName() == LangOpts.CurrentModule ||
+  return M->getTopLevelPrimaryModuleName() == SemaRef.getCurrentModuleName() ||
          (M->Kind == Module::GlobalModuleFragment && !M->Parent);
 }
 
@@ -1574,7 +1574,7 @@
 
 bool Sema::hasMergedDefinitionInCurrentModule(NamedDecl *Def) {
   for (const Module *Merged : Context.getModulesWithMergedDefinition(Def))
-    if (isInCurrentModule(Merged, getLangOpts()))
+    if (isInCurrentModule(Merged, *this))
       return true;
   return false;
 }
@@ -1756,7 +1756,7 @@
   // means it is part of the current module. For any other query, that means it
   // is in our visible module set.
   if (ModulePrivate) {
-    if (isInCurrentModule(M, getLangOpts()))
+    if (isInCurrentModule(M, *this))
       return true;
   } else {
     if (VisibleModules.isVisible(M))
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -1599,6 +1599,12 @@
   if (NewM == OldM)
     return false;
 
+  // If NewM and OldM are two partitions of the same module so the redeclaration
+  // should be compatible.
+  if (NewM && OldM &&
+      NewM->getPrimaryModuleName() == OldM->getPrimaryModuleName())
+    return false;
+
   bool NewIsModuleInterface = NewM && NewM->isModulePurview();
   bool OldIsModuleInterface = OldM && OldM->isModulePurview();
   if (NewIsModuleInterface || OldIsModuleInterface) {
Index: clang/lib/Parse/Parser.cpp
===================================================================
--- clang/lib/Parse/Parser.cpp
+++ clang/lib/Parse/Parser.cpp
@@ -2341,17 +2341,14 @@
   if (ParseModuleName(ModuleLoc, Path, /*IsImport*/false))
     return nullptr;
 
+  SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Partition;
   // Parse the optional module-partition.
   if (Tok.is(tok::colon)) {
-    SourceLocation ColonLoc = ConsumeToken();
-    SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Partition;
+    ConsumeToken();
     if (ParseModuleName(ModuleLoc, Partition, /*IsImport*/false))
       return nullptr;
 
-    // FIXME: Support module partition declarations.
-    Diag(ColonLoc, diag::err_unsupported_module_partition)
-      << SourceRange(ColonLoc, Partition.back().second);
-    // Recover by parsing as a non-partition.
+    MDK = Sema::ModuleDeclKind::Partition;
   }
 
   // We don't support any module attributes yet; just parse them and diagnose.
@@ -2361,7 +2358,8 @@
 
   ExpectAndConsumeSemi(diag::err_module_expected_semi);
 
-  return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, IsFirstDecl);
+  return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, Partition,
+                                 IsFirstDecl);
 }
 
 /// Parse a module import declaration. This is essentially the same for
@@ -2393,6 +2391,7 @@
 
   SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
   Module *HeaderUnit = nullptr;
+  bool IsPartition = false;
 
   if (Tok.is(tok::header_name)) {
     // This is a header import that the preprocessor decided we should skip
@@ -2404,14 +2403,11 @@
     HeaderUnit = reinterpret_cast<Module *>(Tok.getAnnotationValue());
     ConsumeAnnotationToken();
   } else if (getLangOpts().CPlusPlusModules && Tok.is(tok::colon)) {
-    SourceLocation ColonLoc = ConsumeToken();
+    ConsumeToken();
     if (ParseModuleName(ImportLoc, Path, /*IsImport*/true))
       return nullptr;
 
-    // FIXME: Support module partition import.
-    Diag(ColonLoc, diag::err_unsupported_module_partition)
-      << SourceRange(ColonLoc, Path.back().second);
-    return nullptr;
+    IsPartition = true;
   } else {
     if (ParseModuleName(ImportLoc, Path, /*IsImport*/true))
       return nullptr;
@@ -2433,7 +2429,8 @@
     Import =
         Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, HeaderUnit);
   else if (!Path.empty())
-    Import = Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Path);
+    Import = Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Path,
+                                       IsPartition);
   ExpectAndConsumeSemi(diag::err_module_expected_semi);
   if (Import.isInvalid())
     return nullptr;
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -2215,6 +2215,8 @@
   Module *getOwningModule(const Decl *Entity) {
     return Entity->getOwningModule();
   }
+  StringRef getCurrentModuleName() const;
+  bool IsCurrentModulePartition() const;
 
   /// Make a merged definition of an existing hidden definition \p ND
   /// visible at the specified location.
@@ -2915,13 +2917,16 @@
   enum class ModuleDeclKind {
     Interface,      ///< 'export module X;'
     Implementation, ///< 'module X;'
+    Partition,      ///< 'export module X:Y;'
+                    ///< 'module X:Y;'
   };
 
   /// The parser has processed a module-declaration that begins the definition
   /// of a module interface or implementation.
   DeclGroupPtrTy ActOnModuleDecl(SourceLocation StartLoc,
                                  SourceLocation ModuleLoc, ModuleDeclKind MDK,
-                                 ModuleIdPath Path, bool IsFirstDecl);
+                                 ModuleIdPath Path, ModuleIdPath PartitionPath,
+                                 bool IsFirstDecl);
 
   /// The parser has processed a global-module-fragment declaration that begins
   /// the definition of the global module fragment of the current module unit.
@@ -2944,11 +2949,13 @@
   /// \param Path The module access path.
   DeclResult ActOnModuleImport(SourceLocation StartLoc,
                                SourceLocation ExportLoc,
-                               SourceLocation ImportLoc, ModuleIdPath Path);
+                               SourceLocation ImportLoc, ModuleIdPath Path,
+                               bool IsPartition = false);
   DeclResult ActOnModuleImport(SourceLocation StartLoc,
                                SourceLocation ExportLoc,
                                SourceLocation ImportLoc, Module *M,
-                               ModuleIdPath Path = {});
+                               ModuleIdPath Path = {},
+                               bool IsPartition = false);
 
   /// The parser has processed a module import translated from a
   /// #include or similar preprocessing directive.
Index: clang/include/clang/Basic/Module.h
===================================================================
--- clang/include/clang/Basic/Module.h
+++ clang/include/clang/Basic/Module.h
@@ -530,6 +530,18 @@
     return getTopLevelModule()->Name;
   }
 
+  /// Retrieve the name which contains a C++20 module partitions.
+  StringRef getTopLevelPrimaryModuleName() const {
+    if (Kind != ModuleInterfaceUnit)
+      return getTopLevelModuleName();
+    return getTopLevelModuleName().split('-').first;
+  }
+  StringRef getPrimaryModuleName() const {
+    if (Kind != ModuleInterfaceUnit)
+      return Name;
+    return StringRef(Name).split('-').first;
+  }
+
   /// The serialized AST file for this module, if one was created.
   OptionalFileEntryRefDegradesToFileEntryPtr getASTFile() const {
     return getTopLevelModule()->ASTFile;
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10933,6 +10933,11 @@
   "import of module '%0' appears within same top-level module '%1'">;
 def err_module_import_in_implementation : Error<
   "@import of module '%0' in implementation of '%1'; use #import">;
+def err_module_import_in_non_module : Error <
+   "import module partition isn't allowed in non module.">;
+def err_private_module_fragment_in_module_partition : Error <
+   "private module fragment is not allowed to appear in a module partition "
+   "since a module unit with a private-module-fragment shall be the only module unit of its module.">;
 
 // C++ Modules
 def err_module_decl_not_at_start : Error<
Index: clang/include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticParseKinds.td
+++ clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1524,8 +1524,6 @@
 def err_private_module_fragment_expected_semi : Error<
   "expected ';' after private module fragment declaration">;
 def err_missing_before_module_end : Error<"expected %0 at end of module">;
-def err_unsupported_module_partition : Error<
-  "sorry, module partitions are not yet supported">;
 
 def err_export_empty : Error<"export declaration cannot be empty">;
 }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to