diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2946,8 +2946,10 @@ 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 --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -110,9 +110,24 @@ // 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 @@ 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 @@ // 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 @@ 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 @@ // 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 @@ // 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 --- /dev/null +++ b/clang/test/Modules/cxx20-10-1-ex1.cpp @@ -0,0 +1,50 @@ +// The example in the standard is not in required build order. +// revised here + +// RUN: rm -rf %t +// RUN: mkdir -p %t + +// RUN: %clang_cc1 -std=c++20 -emit-module-interface -D TU=1 -x c++ %s \ +// RUN: -o %t/A_Internals.pcm + +// RUN: %clang_cc1 -std=c++20 -emit-module-interface -D TU=2 -x c++ %s \ +// RUN: -fmodule-file=%t/A_Internals.pcm -o %t/A_Foo.pcm + +// RUN: %clang_cc1 -std=c++20 -emit-module-interface -D TU=3 -x c++ %s \ +// RUN: -fmodule-file=%t/A_Foo.pcm -o %t/A.pcm + +// RUN: %clang_cc1 -std=c++20 -emit-obj -D TU=4 -x c++ %s \ +// RUN: -fmodule-file=%t/A.pcm -o %t/ex1.o + +// expected-no-diagnostics + +#if TU == 1 + +module A:Internals; +int bar(); + +#elif TU == 2 + +export module A:Foo; + +import :Internals; + +export int foo() { return 2 * (bar() + 1); } + +#elif TU == 3 + +export module A; +export import :Foo; +export int baz(); + +#elif TU == 4 +module A; + +import :Internals; + +int bar() { return baz() - 10; } +int baz() { return 30; } + +#else +#error "no TU set" +#endif diff --git a/clang/test/Modules/cxx20-10-1-ex2.cpp b/clang/test/Modules/cxx20-10-1-ex2.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/cxx20-10-1-ex2.cpp @@ -0,0 +1,60 @@ + +// RUN: rm -rf %t +// RUN: mkdir -p %t + +// RUN: %clang_cc1 -std=c++20 -emit-module-interface -D TU=1 -x c++ %s \ +// RUN: -o %t/B_Y.pcm + +// RUN: %clang_cc1 -std=c++20 -emit-module-interface -D TU=2 -x c++ %s \ +// RUN: -fmodule-file=%t/B_Y.pcm -o %t/B.pcm + +// RUN: %clang_cc1 -std=c++20 -emit-module-interface -D TU=3 -x c++ %s \ +// RUN: -o %t/B_X1.pcm -verify + +// Not expected to work yet. +// %clang_cc1 -std=c++20 -emit-module-interface -D TU=4 -x c++ %s \ +// -fmodule-file=%t/B.pcm -o %t/B_X2.pcm + +// RUN: %clang_cc1 -std=c++20 -emit-obj -D TU=5 -x c++ %s \ +// RUN: -fmodule-file=%t/B.pcm -o %t/b_tu5.s + +// RUN: %clang_cc1 -std=c++20 -S -D TU=6 -x c++ %s \ +// 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 -D TU=7 -x c++ %s \ +// -fmodule-file=%t/B_X2.pcm -o %t/B_X3.pcm -verify + +#if TU == 1 + module B:Y; + int y(); +// expected-no-diagnostics +#elif TU == 2 + export module B; + import :Y; + int n = y(); +// expected-no-diagnostics +#elif TU == 3 + module B:X1; // does not implicitly import B + int &a = n; // expected-error {{use of undeclared identifier }} +#elif TU == 4 + module B:X2; // does not implicitly import B + import B; + int &b = n; // OK +// expected-no-diagnostics +#elif TU == 5 + module B; // implicitly imports B + int &c = n; // OK +// expected-no-diagnostics +#elif TU == 6 + import B; + // error, n is module-local and this is not a module. + int &c = n; // expected-error {{use of undeclared identifier}} +#elif TU == 7 + 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 }} +#else +#error "no TU set" +#endif diff --git a/clang/test/Modules/cxx20-import-diagnostics-a.cpp b/clang/test/Modules/cxx20-import-diagnostics-a.cpp --- a/clang/test/Modules/cxx20-import-diagnostics-a.cpp +++ b/clang/test/Modules/cxx20-import-diagnostics-a.cpp @@ -94,9 +94,9 @@ #elif TU == 6 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;