[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
This commit is contained in:
Chuanqi Xu 2021-12-08 11:34:18 +08:00
parent ec64d10340
commit e587372f85
19 changed files with 211 additions and 17 deletions

View File

@ -153,6 +153,10 @@ public:
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;

View File

@ -538,8 +538,11 @@ public:
///
/// 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,

View File

@ -2222,6 +2222,11 @@ private:
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:

View File

@ -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 *

View File

@ -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;
}

View File

@ -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();
}

View File

@ -0,0 +1,8 @@
module;
#include "h2.h"
export module X;
extern "C++" class CPP {
public:
void print() {}
};

View File

@ -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();
}

View File

@ -0,0 +1 @@
extern "C++" class CPP;

View File

@ -0,0 +1 @@
extern "C" struct C;

View File

@ -0,0 +1 @@
extern "C++" int a;

View File

@ -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;
}
}

View File

@ -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 {};

View File

@ -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 {};

View File

@ -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;
};

View File

@ -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;

View File

@ -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@-1 {{'CPP' must be defined before it is used}}
// expected-error@-2 {{no member named 'print' in 'CPP'}}
// expected-note@Inputs/CPP.cppm:5 {{definition here is not reachable}}
// expected-note@Inputs/CPP.cppm:5 {{definition here is not reachable}}
}

View File

@ -0,0 +1,7 @@
extern "C" void foo();
extern "C" {
void bar();
int baz();
double double_func();
}
extern "C++" class CPP;

View File

@ -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;
}
}