[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