From 6114491441700cc8a614d284407e9a6e9bf74751 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Sun, 7 Feb 2021 17:08:42 +0000 Subject: [PATCH] [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 --- clang/include/clang/Sema/Sema.h | 6 +- clang/lib/Sema/SemaModule.cpp | 70 ++++++++++++++--------- clang/test/Modules/cxx20-10-1-ex1.cpp | 48 ++++++++++++++++ clang/test/Modules/cxx20-10-1-ex2.cpp | 64 +++++++++++++++++++++ clang/test/Modules/cxx20-import-diagnostics-a.cpp | 4 +- 5 files changed, 160 insertions(+), 32 deletions(-) create mode 100644 clang/test/Modules/cxx20-10-1-ex1.cpp create mode 100644 clang/test/Modules/cxx20-10-1-ex2.cpp diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 436718a..0b872c4 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -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 diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp index a797644..b251507 100644 --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -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 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); diff --git a/clang/test/Modules/cxx20-10-1-ex1.cpp b/clang/test/Modules/cxx20-10-1-ex1.cpp new file mode 100644 index 0000000..b9a5e80 --- /dev/null +++ b/clang/test/Modules/cxx20-10-1-ex1.cpp @@ -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; } diff --git a/clang/test/Modules/cxx20-10-1-ex2.cpp b/clang/test/Modules/cxx20-10-1-ex2.cpp new file mode 100644 index 0000000..66323fc --- /dev/null +++ b/clang/test/Modules/cxx20-10-1-ex2.cpp @@ -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 }} diff --git a/clang/test/Modules/cxx20-import-diagnostics-a.cpp b/clang/test/Modules/cxx20-import-diagnostics-a.cpp index 8e2940a..8017e19 100644 --- a/clang/test/Modules/cxx20-import-diagnostics-a.cpp +++ b/clang/test/Modules/cxx20-import-diagnostics-a.cpp @@ -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; -- 2.7.4