Skip to content

Commit

Permalink
[C++20] [Module] Support extern C/C++ semantics
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ChuanqiXu9 committed Dec 8, 2021
1 parent ec64d10 commit e587372
Show file tree
Hide file tree
Showing 19 changed files with 211 additions and 17 deletions.
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/Module.h
Expand Up @@ -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;
Expand Down
7 changes: 5 additions & 2 deletions clang/include/clang/Lex/ModuleMap.h
Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -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:
Expand Down
16 changes: 10 additions & 6 deletions clang/lib/Lex/ModuleMap.cpp
Expand Up @@ -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 *
Expand Down
18 changes: 18 additions & 0 deletions clang/lib/Sema/SemaDeclCXX.cpp
Expand Up @@ -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;
Expand All @@ -16162,6 +16176,10 @@ Decl *Sema::ActOnFinishLinkageSpecification(Scope *S,
LinkageSpecDecl* LSDecl = cast<LinkageSpecDecl>(LinkageSpec);
LSDecl->setRBraceLoc(RBraceLoc);
}

if (getLangOpts().CPlusPlusModules)
PopGlobalModuleFragment();

PopDeclContext();
return LinkageSpec;
}
Expand Down
33 changes: 24 additions & 9 deletions clang/lib/Sema/SemaModule.cpp
Expand Up @@ -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();
Expand Down Expand Up @@ -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();
}
8 changes: 8 additions & 0 deletions 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() {}
};
12 changes: 12 additions & 0 deletions 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();
}
1 change: 1 addition & 0 deletions clang/test/CXX/module/module.unit/p7/Inputs/h2.h
@@ -0,0 +1 @@
extern "C++" class CPP;
1 change: 1 addition & 0 deletions clang/test/CXX/module/module.unit/p7/Inputs/h4.h
@@ -0,0 +1 @@
extern "C" struct C;
1 change: 1 addition & 0 deletions clang/test/CXX/module/module.unit/p7/Inputs/h5.h
@@ -0,0 +1 @@
extern "C++" int a;
35 changes: 35 additions & 0 deletions 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;
}
}
9 changes: 9 additions & 0 deletions 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 {};
7 changes: 7 additions & 0 deletions 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 {};
13 changes: 13 additions & 0 deletions 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;
};
9 changes: 9 additions & 0 deletions 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;
15 changes: 15 additions & 0 deletions 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@-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}}
}
7 changes: 7 additions & 0 deletions 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;
27 changes: 27 additions & 0 deletions 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;
}
}

0 comments on commit e587372

Please sign in to comment.