[C++20][Modules][4/8] Handle generation of partition implementation CMIs.
Partition implementations are special, they generate a CMI, but it does not have an 'export' line, and we cannot export anything from the it [that is it can only make decls available to other members of the owning module, not to importers of that]. Add initial testcases for partition handling, derived from the examples in Section 10 of the C++20 standard, which identifies what should be accepted and/or rejected. Differential Revision: https://reviews.llvm.org/D118587
This commit is contained in:
parent
2cd675249d
commit
6114491441
|
@ -2943,8 +2943,10 @@ public:
|
|||
SourceLocation SemiLoc);
|
||||
|
||||
enum class ModuleDeclKind {
|
||||
Interface, ///< 'export module X;'
|
||||
Implementation, ///< 'module X;'
|
||||
Interface, ///< 'export module X;'
|
||||
Implementation, ///< 'module X;'
|
||||
PartitionInterface, ///< 'export module X:Y;'
|
||||
PartitionImplementation, ///< 'module X:Y;'
|
||||
};
|
||||
|
||||
/// An enumeration to represent the transition of states in parsing module
|
||||
|
|
|
@ -110,9 +110,24 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
|
|||
// module state;
|
||||
ImportState = ModuleImportState::NotACXX20Module;
|
||||
|
||||
// A module implementation unit requires that we are not compiling a module
|
||||
// of any kind. A module interface unit requires that we are not compiling a
|
||||
// module map.
|
||||
bool IsPartition = !Partition.empty();
|
||||
if (IsPartition)
|
||||
switch (MDK) {
|
||||
case ModuleDeclKind::Implementation:
|
||||
MDK = ModuleDeclKind::PartitionImplementation;
|
||||
break;
|
||||
case ModuleDeclKind::Interface:
|
||||
MDK = ModuleDeclKind::PartitionInterface;
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("how did we get a partition type set?");
|
||||
}
|
||||
|
||||
// A (non-partition) module implementation unit requires that we are not
|
||||
// compiling a module of any kind. A partition implementation emits an
|
||||
// interface (and the AST for the implementation), which will subsequently
|
||||
// be consumed to emit a binary.
|
||||
// A module interface unit requires that we are not compiling a module map.
|
||||
switch (getLangOpts().getCompilingModule()) {
|
||||
case LangOptions::CMK_None:
|
||||
// It's OK to compile a module interface as a normal translation unit.
|
||||
|
@ -123,7 +138,7 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
|
|||
break;
|
||||
|
||||
// We were asked to compile a module interface unit but this is a module
|
||||
// implementation unit. That indicates the 'export' is missing.
|
||||
// implementation unit.
|
||||
Diag(ModuleLoc, diag::err_module_interface_implementation_mismatch)
|
||||
<< FixItHint::CreateInsertion(ModuleLoc, "export ");
|
||||
MDK = ModuleDeclKind::Interface;
|
||||
|
@ -180,7 +195,6 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
|
|||
// modules, the dots here are just another character that can appear in a
|
||||
// module name.
|
||||
std::string ModuleName = stringFromPath(Path);
|
||||
bool IsPartition = !Partition.empty();
|
||||
if (IsPartition) {
|
||||
ModuleName += ":";
|
||||
ModuleName += stringFromPath(Partition);
|
||||
|
@ -202,7 +216,8 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
|
|||
Module *Mod;
|
||||
|
||||
switch (MDK) {
|
||||
case ModuleDeclKind::Interface: {
|
||||
case ModuleDeclKind::Interface:
|
||||
case ModuleDeclKind::PartitionInterface: {
|
||||
// We can't have parsed or imported a definition of this module or parsed a
|
||||
// module map defining it already.
|
||||
if (auto *M = Map.findModule(ModuleName)) {
|
||||
|
@ -219,36 +234,36 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
|
|||
// Create a Module for the module that we're defining.
|
||||
Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName,
|
||||
GlobalModuleFragment);
|
||||
if (IsPartition)
|
||||
if (MDK == ModuleDeclKind::PartitionInterface)
|
||||
Mod->Kind = Module::ModulePartitionInterface;
|
||||
assert(Mod && "module creation should not fail");
|
||||
break;
|
||||
}
|
||||
|
||||
case ModuleDeclKind::Implementation:
|
||||
case ModuleDeclKind::Implementation: {
|
||||
std::pair<IdentifierInfo *, SourceLocation> ModuleNameLoc(
|
||||
PP.getIdentifierInfo(ModuleName), Path[0].second);
|
||||
if (IsPartition) {
|
||||
// Create an interface, but note that it is an implementation
|
||||
// unit.
|
||||
// C++20 A module-declaration that contains neither an export-
|
||||
// keyword nor a module-partition implicitly imports the primary
|
||||
// module interface unit of the module as if by a module-import-
|
||||
// declaration.
|
||||
Mod = getModuleLoader().loadModule(ModuleLoc, {ModuleNameLoc},
|
||||
Module::AllVisible,
|
||||
/*IsInclusionDirective=*/false);
|
||||
if (!Mod) {
|
||||
Diag(ModuleLoc, diag::err_module_not_defined) << ModuleName;
|
||||
// Create an empty module interface unit for error recovery.
|
||||
Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName,
|
||||
GlobalModuleFragment);
|
||||
Mod->Kind = Module::ModulePartitionImplementation;
|
||||
} else {
|
||||
// C++20 A module-declaration that contains neither an export-
|
||||
// keyword nor a module-partition implicitly imports the primary
|
||||
// module interface unit of the module as if by a module-import-
|
||||
// declaration.
|
||||
Mod = getModuleLoader().loadModule(ModuleLoc, {ModuleNameLoc},
|
||||
Module::AllVisible,
|
||||
/*IsInclusionDirective=*/false);
|
||||
if (!Mod) {
|
||||
Diag(ModuleLoc, diag::err_module_not_defined) << ModuleName;
|
||||
// Create an empty module interface unit for error recovery.
|
||||
Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName,
|
||||
GlobalModuleFragment);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case ModuleDeclKind::PartitionImplementation:
|
||||
// Create an interface, but note that it is an implementation
|
||||
// unit.
|
||||
Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName,
|
||||
GlobalModuleFragment);
|
||||
Mod->Kind = Module::ModulePartitionImplementation;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -264,8 +279,7 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
|
|||
// Switch from the global module fragment (if any) to the named module.
|
||||
ModuleScopes.back().BeginLoc = StartLoc;
|
||||
ModuleScopes.back().Module = Mod;
|
||||
ModuleScopes.back().ModuleInterface =
|
||||
(MDK != ModuleDeclKind::Implementation || IsPartition);
|
||||
ModuleScopes.back().ModuleInterface = MDK != ModuleDeclKind::Implementation;
|
||||
ModuleScopes.back().IsPartition = IsPartition;
|
||||
VisibleModules.setVisible(Mod, ModuleLoc);
|
||||
|
||||
|
|
48
clang/test/Modules/cxx20-10-1-ex1.cpp
Normal file
48
clang/test/Modules/cxx20-10-1-ex1.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
// The example in the standard is not in required build order.
|
||||
// revised here
|
||||
|
||||
// RUN: rm -rf %t
|
||||
// RUN: mkdir -p %t
|
||||
// RUN: split-file %s %t
|
||||
|
||||
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/std10-1-ex1-tu1.cpp \
|
||||
// RUN: -o %t/A_Internals.pcm
|
||||
|
||||
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/std10-1-ex1-tu2.cpp \
|
||||
// RUN: -fmodule-file=%t/A_Internals.pcm -o %t/A_Foo.pcm
|
||||
|
||||
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/std10-1-ex1-tu3.cpp \
|
||||
// RUN: -fmodule-file=%t/A_Foo.pcm -o %t/A.pcm
|
||||
|
||||
// RUN: %clang_cc1 -std=c++20 -emit-obj %t/std10-1-ex1-tu4.cpp \
|
||||
// RUN: -fmodule-file=%t/A.pcm -o %t/ex1.o
|
||||
|
||||
// expected-no-diagnostics
|
||||
|
||||
//--- std10-1-ex1-tu1.cpp
|
||||
|
||||
module A:Internals;
|
||||
int bar();
|
||||
|
||||
//--- std10-1-ex1-tu2.cpp
|
||||
|
||||
export module A:Foo;
|
||||
|
||||
import :Internals;
|
||||
|
||||
export int foo() { return 2 * (bar() + 1); }
|
||||
|
||||
//--- std10-1-ex1-tu3.cpp
|
||||
|
||||
export module A;
|
||||
export import :Foo;
|
||||
export int baz();
|
||||
|
||||
//--- std10-1-ex1-tu4.cpp
|
||||
|
||||
module A;
|
||||
|
||||
import :Internals;
|
||||
|
||||
int bar() { return baz() - 10; }
|
||||
int baz() { return 30; }
|
64
clang/test/Modules/cxx20-10-1-ex2.cpp
Normal file
64
clang/test/Modules/cxx20-10-1-ex2.cpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
|
||||
// RUN: rm -rf %t
|
||||
// RUN: mkdir -p %t
|
||||
// RUN: split-file %s %t
|
||||
|
||||
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/std10-1-ex2-tu1.cpp \
|
||||
// RUN: -o %t/B_Y.pcm
|
||||
|
||||
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/std10-1-ex2-tu2.cpp \
|
||||
// RUN: -fmodule-file=%t/B_Y.pcm -o %t/B.pcm
|
||||
|
||||
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/std10-1-ex2-tu3.cpp \
|
||||
// RUN: -o %t/B_X1.pcm -verify
|
||||
|
||||
// Not expected to work yet.
|
||||
// %clang_cc1 -std=c++20 -emit-module-interface %t/std10-1-ex2-tu4.cpp \
|
||||
// -fmodule-file=%t/B.pcm -o %t/B_X2.pcm
|
||||
|
||||
// RUN: %clang_cc1 -std=c++20 -emit-obj %t/std10-1-ex2-tu5.cpp \
|
||||
// RUN: -fmodule-file=%t/B.pcm -o %t/b_tu5.o
|
||||
|
||||
// RUN: %clang_cc1 -std=c++20 -S %t/std10-1-ex2-tu6.cpp \
|
||||
// RUN: -fmodule-file=%t/B.pcm -o %t/b_tu6.s -verify
|
||||
|
||||
// Not expected to work yet.
|
||||
// %clang_cc1 -std=c++20 -emit-module-interface %t/std10-1-ex2-tu7.cpp \
|
||||
// -fmodule-file=%t/B_X2.pcm -o %t/B_X3.pcm -verify
|
||||
|
||||
//--- std10-1-ex2-tu1.cpp
|
||||
module B:Y;
|
||||
int y();
|
||||
// expected-no-diagnostics
|
||||
|
||||
//--- std10-1-ex2-tu2.cpp
|
||||
export module B;
|
||||
import :Y;
|
||||
int n = y();
|
||||
// expected-no-diagnostics
|
||||
|
||||
//--- std10-1-ex2-tu3.cpp
|
||||
module B:X1; // does not implicitly import B
|
||||
int &a = n; // expected-error {{use of undeclared identifier }}
|
||||
|
||||
//--- std10-1-ex2-tu4.cpp
|
||||
module B:X2; // does not implicitly import B
|
||||
import B;
|
||||
int &b = n; // OK
|
||||
// expected-no-diagnostics
|
||||
|
||||
//--- std10-1-ex2-tu5.cpp
|
||||
module B; // implicitly imports B
|
||||
int &c = n; // OK
|
||||
// expected-no-diagnostics
|
||||
|
||||
//--- std10-1-ex2-tu6.cpp
|
||||
import B;
|
||||
// error, n is module-local and this is not a module.
|
||||
int &c = n; // expected-error {{use of undeclared identifier}}
|
||||
|
||||
//--- std10-1-ex2-tu7.cpp
|
||||
module B:X3; // does not implicitly import B
|
||||
import :X2; // X2 is an implementation so exports nothing.
|
||||
// error: n not visible here.
|
||||
int &c = n; // expected-error {{use of undeclared identifier }}
|
|
@ -95,9 +95,9 @@ import C; // expected-error {{imports must immediately follow the module declara
|
|||
//--- import-diags-tu7.cpp
|
||||
|
||||
module;
|
||||
// We can only have preprocessor commands here, which could include an include
|
||||
// We can only have preprocessor directives here, which permits an include-
|
||||
// translated header unit. However those are identified specifically by the
|
||||
// preprocessor; non-preprocessed user code should not contain an import here.
|
||||
// preprocessor; non-preprocessed user code should not contain an 'import' here.
|
||||
import B; // expected-error {{module imports cannot be in the global module fragment}}
|
||||
|
||||
export module D;
|
||||
|
|
Loading…
Reference in a new issue