[clang] e587372 - [C++20] [Module] Support extern C/C++ semantics

Chuanqi Xu via cfe-commits cfe-commits at lists.llvm.org
Tue Dec 7 21:29:43 PST 2021


Author: Chuanqi Xu
Date: 2021-12-08T13:29:16+08:00
New Revision: e587372f85105b85ae790fbe129b5d609d6dfb76

URL: https://github.com/llvm/llvm-project/commit/e587372f85105b85ae790fbe129b5d609d6dfb76
DIFF: https://github.com/llvm/llvm-project/commit/e587372f85105b85ae790fbe129b5d609d6dfb76.diff

LOG: [C++20] [Module] Support extern C/C++ semantics

According to [module.unit]p7.2.3, a declaration within a linkage-specification
should be attached to the global module.
This let user to forward declare types across modules.

Reviewed by: rsmith, aaron.ballman

Differential Revision: https://reviews.llvm.org/D110215

Added: 
    clang/test/CXX/module/module.unit/p7/Inputs/CPP.cppm
    clang/test/CXX/module/module.unit/p7/Inputs/h1.h
    clang/test/CXX/module/module.unit/p7/Inputs/h2.h
    clang/test/CXX/module/module.unit/p7/Inputs/h4.h
    clang/test/CXX/module/module.unit/p7/Inputs/h5.h
    clang/test/CXX/module/module.unit/p7/t1.cpp
    clang/test/CXX/module/module.unit/p7/t2.cpp
    clang/test/CXX/module/module.unit/p7/t3.cpp
    clang/test/CXX/module/module.unit/p7/t4.cpp
    clang/test/CXX/module/module.unit/p7/t5.cpp
    clang/test/CXX/module/module.unit/p7/t6.cpp
    clang/test/CodeGenCXX/Inputs/module-extern-C.h
    clang/test/CodeGenCXX/module-extern-C.cpp

Modified: 
    clang/include/clang/Basic/Module.h
    clang/include/clang/Lex/ModuleMap.h
    clang/include/clang/Sema/Sema.h
    clang/lib/Lex/ModuleMap.cpp
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/lib/Sema/SemaModule.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h
index 3476b05d2e929..de7857347bc2e 100644
--- a/clang/include/clang/Basic/Module.h
+++ b/clang/include/clang/Basic/Module.h
@@ -153,6 +153,10 @@ class Module {
     return Kind == ModuleInterfaceUnit || Kind == PrivateModuleFragment;
   }
 
+  /// Does this Module scope describe a fragment of the global module within
+  /// some C++ module.
+  bool isGlobalModule() const { return Kind == GlobalModuleFragment; }
+
 private:
   /// The submodules of this module, indexed by name.
   std::vector<Module *> SubModules;

diff  --git a/clang/include/clang/Lex/ModuleMap.h b/clang/include/clang/Lex/ModuleMap.h
index 41f85a1f572d7..08c61a5dc5607 100644
--- a/clang/include/clang/Lex/ModuleMap.h
+++ b/clang/include/clang/Lex/ModuleMap.h
@@ -538,8 +538,11 @@ class ModuleMap {
   ///
   /// We model the global module fragment as a submodule of the module
   /// interface unit. Unfortunately, we can't create the module interface
-  /// unit's Module until later, because we don't know what it will be called.
-  Module *createGlobalModuleFragmentForModuleUnit(SourceLocation Loc);
+  /// unit's Module until later, because we don't know what it will be called
+  /// usually. See C++20 [module.unit]/7.2 for the case we could know its
+  /// parent.
+  Module *createGlobalModuleFragmentForModuleUnit(SourceLocation Loc,
+                                                  Module *Parent = nullptr);
 
   /// Create a global module fragment for a C++ module interface unit.
   Module *createPrivateModuleFragmentForInterfaceUnit(Module *Parent,

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 8182d25295c5f..abecf68e0a811 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2222,6 +2222,11 @@ class Sema final {
     return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module;
   }
 
+  /// Enter the scope of the global module.
+  Module *PushGlobalModuleFragment(SourceLocation BeginLoc, bool IsImplicit);
+  /// Leave the scope of the global module.
+  void PopGlobalModuleFragment();
+
   VisibleModuleSet VisibleModules;
 
 public:

diff  --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp
index 9fa170410da3c..0b136aeb580fe 100644
--- a/clang/lib/Lex/ModuleMap.cpp
+++ b/clang/lib/Lex/ModuleMap.cpp
@@ -832,12 +832,16 @@ std::pair<Module *, bool> ModuleMap::findOrCreateModule(StringRef Name,
   return std::make_pair(Result, true);
 }
 
-Module *ModuleMap::createGlobalModuleFragmentForModuleUnit(SourceLocation Loc) {
-  PendingSubmodules.emplace_back(
-      new Module("<global>", Loc, nullptr, /*IsFramework*/ false,
-                 /*IsExplicit*/ true, NumCreatedModules++));
-  PendingSubmodules.back()->Kind = Module::GlobalModuleFragment;
-  return PendingSubmodules.back().get();
+Module *ModuleMap::createGlobalModuleFragmentForModuleUnit(SourceLocation Loc,
+                                                           Module *Parent) {
+  auto *Result = new Module("<global>", Loc, Parent, /*IsFramework*/ false,
+                            /*IsExplicit*/ true, NumCreatedModules++);
+  Result->Kind = Module::GlobalModuleFragment;
+  // If the created module isn't owned by a parent, send it to PendingSubmodules
+  // to wait for its parent.
+  if (!Result->Parent)
+    PendingSubmodules.emplace_back(Result);
+  return Result;
 }
 
 Module *

diff  --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 2658e96986887..558ee3fd8a398 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -16146,6 +16146,20 @@ Decl *Sema::ActOnStartLinkageSpecification(Scope *S, SourceLocation ExternLoc,
   LinkageSpecDecl *D = LinkageSpecDecl::Create(Context, CurContext, ExternLoc,
                                                LangStr->getExprLoc(), Language,
                                                LBraceLoc.isValid());
+
+  /// C++ [module.unit]p7.2.3
+  /// - Otherwise, if the declaration
+  ///   - ...
+  ///   - ...
+  ///   - appears within a linkage-specification,
+  ///   it is attached to the global module.
+  if (getLangOpts().CPlusPlusModules) {
+    Module *GlobalModule =
+        PushGlobalModuleFragment(ExternLoc, /*IsImplicit=*/true);
+    D->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ModulePrivate);
+    D->setLocalOwningModule(GlobalModule);
+  }
+
   CurContext->addDecl(D);
   PushDeclContext(S, D);
   return D;
@@ -16162,6 +16176,10 @@ Decl *Sema::ActOnFinishLinkageSpecification(Scope *S,
     LinkageSpecDecl* LSDecl = cast<LinkageSpecDecl>(LinkageSpec);
     LSDecl->setRBraceLoc(RBraceLoc);
   }
+
+  if (getLangOpts().CPlusPlusModules)
+    PopGlobalModuleFragment();
+
   PopDeclContext();
   return LinkageSpec;
 }

diff  --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp
index af95b1a93cc45..34a6c6eae33f4 100644
--- a/clang/lib/Sema/SemaModule.cpp
+++ b/clang/lib/Sema/SemaModule.cpp
@@ -68,15 +68,8 @@ Sema::ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc) {
 
   // We start in the global module; all those declarations are implicitly
   // module-private (though they do not have module linkage).
-  auto &Map = PP.getHeaderSearchInfo().getModuleMap();
-  auto *GlobalModule = Map.createGlobalModuleFragmentForModuleUnit(ModuleLoc);
-  assert(GlobalModule && "module creation should not fail");
-
-  // Enter the scope of the global module.
-  ModuleScopes.push_back({});
-  ModuleScopes.back().BeginLoc = ModuleLoc;
-  ModuleScopes.back().Module = GlobalModule;
-  VisibleModules.setVisible(GlobalModule, ModuleLoc);
+  Module *GlobalModule =
+      PushGlobalModuleFragment(ModuleLoc, /*IsImplicit=*/false);
 
   // All declarations created from now on are owned by the global module.
   auto *TU = Context.getTranslationUnitDecl();
@@ -708,3 +701,25 @@ Decl *Sema::ActOnFinishExportDecl(Scope *S, Decl *D, SourceLocation RBraceLoc) {
 
   return D;
 }
+
+Module *Sema::PushGlobalModuleFragment(SourceLocation BeginLoc,
+                                       bool IsImplicit) {
+  ModuleMap &Map = PP.getHeaderSearchInfo().getModuleMap();
+  Module *GlobalModule =
+      Map.createGlobalModuleFragmentForModuleUnit(BeginLoc, getCurrentModule());
+  assert(GlobalModule && "module creation should not fail");
+
+  // Enter the scope of the global module.
+  ModuleScopes.push_back({BeginLoc, GlobalModule,
+                          /*ModuleInterface=*/false,
+                          /*ImplicitGlobalModuleFragment=*/IsImplicit});
+  VisibleModules.setVisible(GlobalModule, BeginLoc);
+
+  return GlobalModule;
+}
+
+void Sema::PopGlobalModuleFragment() {
+  assert(!ModuleScopes.empty() && getCurrentModule()->isGlobalModule() &&
+         "left the wrong module scope, which is not global module fragment");
+  ModuleScopes.pop_back();
+}

diff  --git a/clang/test/CXX/module/module.unit/p7/Inputs/CPP.cppm b/clang/test/CXX/module/module.unit/p7/Inputs/CPP.cppm
new file mode 100644
index 0000000000000..377733b61b255
--- /dev/null
+++ b/clang/test/CXX/module/module.unit/p7/Inputs/CPP.cppm
@@ -0,0 +1,8 @@
+module;
+#include "h2.h"
+export module X;
+
+extern "C++" class CPP {
+public:
+    void print() {}
+};

diff  --git a/clang/test/CXX/module/module.unit/p7/Inputs/h1.h b/clang/test/CXX/module/module.unit/p7/Inputs/h1.h
new file mode 100644
index 0000000000000..098e5d60b9189
--- /dev/null
+++ b/clang/test/CXX/module/module.unit/p7/Inputs/h1.h
@@ -0,0 +1,12 @@
+extern "C" void foo();
+extern "C" {
+void bar();
+int baz();
+double double_func();
+}
+
+extern "C++" {
+void bar_cpp();
+int baz_cpp();
+double double_func_cpp();
+}

diff  --git a/clang/test/CXX/module/module.unit/p7/Inputs/h2.h b/clang/test/CXX/module/module.unit/p7/Inputs/h2.h
new file mode 100644
index 0000000000000..37fcac600489d
--- /dev/null
+++ b/clang/test/CXX/module/module.unit/p7/Inputs/h2.h
@@ -0,0 +1 @@
+extern "C++" class CPP;

diff  --git a/clang/test/CXX/module/module.unit/p7/Inputs/h4.h b/clang/test/CXX/module/module.unit/p7/Inputs/h4.h
new file mode 100644
index 0000000000000..0d29804b168a6
--- /dev/null
+++ b/clang/test/CXX/module/module.unit/p7/Inputs/h4.h
@@ -0,0 +1 @@
+extern "C" struct C;

diff  --git a/clang/test/CXX/module/module.unit/p7/Inputs/h5.h b/clang/test/CXX/module/module.unit/p7/Inputs/h5.h
new file mode 100644
index 0000000000000..ade1ce5b681c8
--- /dev/null
+++ b/clang/test/CXX/module/module.unit/p7/Inputs/h5.h
@@ -0,0 +1 @@
+extern "C++" int a;

diff  --git a/clang/test/CXX/module/module.unit/p7/t1.cpp b/clang/test/CXX/module/module.unit/p7/t1.cpp
new file mode 100644
index 0000000000000..dcafb3b60c380
--- /dev/null
+++ b/clang/test/CXX/module/module.unit/p7/t1.cpp
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -std=c++20 %s -verify
+// expected-no-diagnostics
+module;
+
+#include "Inputs/h1.h"
+
+export module x;
+
+extern "C" void foo() {
+  return;
+}
+
+extern "C" {
+void bar() {
+  return;
+}
+int baz() {
+  return 3;
+}
+double double_func() {
+  return 5.0;
+}
+}
+
+extern "C++" {
+void bar_cpp() {
+  return;
+}
+int baz_cpp() {
+  return 3;
+}
+double double_func_cpp() {
+  return 5.0;
+}
+}

diff  --git a/clang/test/CXX/module/module.unit/p7/t2.cpp b/clang/test/CXX/module/module.unit/p7/t2.cpp
new file mode 100644
index 0000000000000..f94ca532e42c0
--- /dev/null
+++ b/clang/test/CXX/module/module.unit/p7/t2.cpp
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -std=c++20 %s -verify
+// expected-no-diagnostics
+module;
+
+#include "Inputs/h2.h"
+
+export module x;
+
+extern "C++" class CPP {};

diff  --git a/clang/test/CXX/module/module.unit/p7/t3.cpp b/clang/test/CXX/module/module.unit/p7/t3.cpp
new file mode 100644
index 0000000000000..7f4a55ccd3c72
--- /dev/null
+++ b/clang/test/CXX/module/module.unit/p7/t3.cpp
@@ -0,0 +1,7 @@
+// This tests whether the global module would be created when the program don't declare it explicitly.
+// RUN: %clang_cc1 -std=c++20 %s -verify
+// expected-no-diagnostics
+export module x;
+
+extern "C" void foo();
+extern "C++" class CPP {};

diff  --git a/clang/test/CXX/module/module.unit/p7/t4.cpp b/clang/test/CXX/module/module.unit/p7/t4.cpp
new file mode 100644
index 0000000000000..4821a6966134d
--- /dev/null
+++ b/clang/test/CXX/module/module.unit/p7/t4.cpp
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -std=c++20 %s -verify
+// expected-no-diagnostics
+module;
+
+#include "Inputs/h4.h"
+
+export module x;
+
+extern "C" struct C {
+  int a;
+  int b;
+  double d;
+};

diff  --git a/clang/test/CXX/module/module.unit/p7/t5.cpp b/clang/test/CXX/module/module.unit/p7/t5.cpp
new file mode 100644
index 0000000000000..1b1f5da0c23eb
--- /dev/null
+++ b/clang/test/CXX/module/module.unit/p7/t5.cpp
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -std=c++20 %s -verify
+// expected-no-diagnostics
+module;
+
+#include "Inputs/h4.h"
+
+export module x;
+
+extern "C++" int a = 5;

diff  --git a/clang/test/CXX/module/module.unit/p7/t6.cpp b/clang/test/CXX/module/module.unit/p7/t6.cpp
new file mode 100644
index 0000000000000..8bd05c298d95d
--- /dev/null
+++ b/clang/test/CXX/module/module.unit/p7/t6.cpp
@@ -0,0 +1,15 @@
+// RUN: rm -fr %t
+// RUN: mkdir %t
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %S/Inputs/CPP.cppm -I%S/Inputs -o %t/X.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %s -verify
+module;
+#include "Inputs/h2.h"
+export module use;
+import X;
+void printX(CPP *cpp) {
+  cpp->print(); // expected-error {{'CPP' must be defined before it is used}}
+                // expected-error at -1 {{'CPP' must be defined before it is used}}
+                // expected-error at -2 {{no member named 'print' in 'CPP'}}
+                // expected-note at Inputs/CPP.cppm:5 {{definition here is not reachable}}
+                // expected-note at Inputs/CPP.cppm:5 {{definition here is not reachable}}
+}

diff  --git a/clang/test/CodeGenCXX/Inputs/module-extern-C.h b/clang/test/CodeGenCXX/Inputs/module-extern-C.h
new file mode 100644
index 0000000000000..a939826e639ee
--- /dev/null
+++ b/clang/test/CodeGenCXX/Inputs/module-extern-C.h
@@ -0,0 +1,7 @@
+extern "C" void foo();
+extern "C" {
+void bar();
+int baz();
+double double_func();
+}
+extern "C++" class CPP;

diff  --git a/clang/test/CodeGenCXX/module-extern-C.cpp b/clang/test/CodeGenCXX/module-extern-C.cpp
new file mode 100644
index 0000000000000..0b4bbc2b8fc59
--- /dev/null
+++ b/clang/test/CodeGenCXX/module-extern-C.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -std=c++20 -emit-llvm -triple %itanium_abi_triple -o - %s | FileCheck %s
+
+module;
+
+#include "Inputs/module-extern-C.h"
+
+export module x;
+
+// CHECK: define dso_local void @foo()
+extern "C" void foo() {
+  return;
+}
+
+extern "C" {
+// CHECK: define dso_local void @bar()
+void bar() {
+  return;
+}
+// CHECK: define dso_local i32 @baz()
+int baz() {
+  return 3;
+}
+// CHECK: define dso_local double @double_func()
+double double_func() {
+  return 5.0;
+}
+}


        


More information about the cfe-commits mailing list