diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1524,8 +1524,6 @@ def err_private_module_fragment_expected_semi : Error< "expected ';' after private module fragment declaration">; def err_missing_before_module_end : Error<"expected %0 at end of module">; -def err_unsupported_module_partition : Error< - "sorry, module partitions are not yet supported">; def err_export_empty : Error<"export declaration cannot be empty">; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10933,6 +10933,11 @@ "import of module '%0' appears within same top-level module '%1'">; def err_module_import_in_implementation : Error< "@import of module '%0' in implementation of '%1'; use #import">; +def err_module_import_in_non_module : Error < + "import module partition isn't allowed in non module.">; +def err_private_module_fragment_in_module_partition : Error < + "private module fragment is not allowed to appear in a module partition " + "since a module unit with a private-module-fragment shall be the only module unit of its module.">; // C++ Modules def err_module_decl_not_at_start : Error< diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h --- a/clang/include/clang/Basic/Module.h +++ b/clang/include/clang/Basic/Module.h @@ -530,6 +530,18 @@ return getTopLevelModule()->Name; } + /// Retrieve the name which contains a C++20 module partitions. + StringRef getTopLevelPrimaryModuleName() const { + if (Kind != ModuleInterfaceUnit) + return getTopLevelModuleName(); + return getTopLevelModuleName().split('-').first; + } + StringRef getPrimaryModuleName() const { + if (Kind != ModuleInterfaceUnit) + return Name; + return StringRef(Name).split('-').first; + } + /// The serialized AST file for this module, if one was created. OptionalFileEntryRefDegradesToFileEntryPtr getASTFile() const { return getTopLevelModule()->ASTFile; 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 @@ -2215,6 +2215,8 @@ Module *getOwningModule(const Decl *Entity) { return Entity->getOwningModule(); } + StringRef getCurrentModuleName() const; + bool IsCurrentModulePartition() const; /// Make a merged definition of an existing hidden definition \p ND /// visible at the specified location. @@ -2915,13 +2917,16 @@ enum class ModuleDeclKind { Interface, ///< 'export module X;' Implementation, ///< 'module X;' + Partition, ///< 'export module X:Y;' + ///< 'module X:Y;' }; /// The parser has processed a module-declaration that begins the definition /// of a module interface or implementation. DeclGroupPtrTy ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, ModuleDeclKind MDK, - ModuleIdPath Path, bool IsFirstDecl); + ModuleIdPath Path, ModuleIdPath PartitionPath, + bool IsFirstDecl); /// The parser has processed a global-module-fragment declaration that begins /// the definition of the global module fragment of the current module unit. @@ -2944,11 +2949,13 @@ /// \param Path The module access path. DeclResult ActOnModuleImport(SourceLocation StartLoc, SourceLocation ExportLoc, - SourceLocation ImportLoc, ModuleIdPath Path); + SourceLocation ImportLoc, ModuleIdPath Path, + bool IsPartition = false); DeclResult ActOnModuleImport(SourceLocation StartLoc, SourceLocation ExportLoc, SourceLocation ImportLoc, Module *M, - ModuleIdPath Path = {}); + ModuleIdPath Path = {}, + bool IsPartition = false); /// The parser has processed a module import translated from a /// #include or similar preprocessing directive. diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -2341,17 +2341,14 @@ if (ParseModuleName(ModuleLoc, Path, /*IsImport*/false)) return nullptr; + SmallVector, 2> Partition; // Parse the optional module-partition. if (Tok.is(tok::colon)) { - SourceLocation ColonLoc = ConsumeToken(); - SmallVector, 2> Partition; + ConsumeToken(); if (ParseModuleName(ModuleLoc, Partition, /*IsImport*/false)) return nullptr; - // FIXME: Support module partition declarations. - Diag(ColonLoc, diag::err_unsupported_module_partition) - << SourceRange(ColonLoc, Partition.back().second); - // Recover by parsing as a non-partition. + MDK = Sema::ModuleDeclKind::Partition; } // We don't support any module attributes yet; just parse them and diagnose. @@ -2361,7 +2358,8 @@ ExpectAndConsumeSemi(diag::err_module_expected_semi); - return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, IsFirstDecl); + return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, Partition, + IsFirstDecl); } /// Parse a module import declaration. This is essentially the same for @@ -2393,6 +2391,7 @@ SmallVector, 2> Path; Module *HeaderUnit = nullptr; + bool IsPartition = false; if (Tok.is(tok::header_name)) { // This is a header import that the preprocessor decided we should skip @@ -2404,14 +2403,11 @@ HeaderUnit = reinterpret_cast(Tok.getAnnotationValue()); ConsumeAnnotationToken(); } else if (getLangOpts().CPlusPlusModules && Tok.is(tok::colon)) { - SourceLocation ColonLoc = ConsumeToken(); + ConsumeToken(); if (ParseModuleName(ImportLoc, Path, /*IsImport*/true)) return nullptr; - // FIXME: Support module partition import. - Diag(ColonLoc, diag::err_unsupported_module_partition) - << SourceRange(ColonLoc, Path.back().second); - return nullptr; + IsPartition = true; } else { if (ParseModuleName(ImportLoc, Path, /*IsImport*/true)) return nullptr; @@ -2433,7 +2429,8 @@ Import = Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, HeaderUnit); else if (!Path.empty()) - Import = Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Path); + Import = Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Path, + IsPartition); ExpectAndConsumeSemi(diag::err_module_expected_semi); if (Import.isInvalid()) return nullptr; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -1599,6 +1599,12 @@ if (NewM == OldM) return false; + // If NewM and OldM are two partitions of the same module so the redeclaration + // should be compatible. + if (NewM && OldM && + NewM->getPrimaryModuleName() == OldM->getPrimaryModuleName()) + return false; + bool NewIsModuleInterface = NewM && NewM->isModulePurview(); bool OldIsModuleInterface = OldM && OldM->isModulePurview(); if (NewIsModuleInterface || OldIsModuleInterface) { diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -1558,10 +1558,10 @@ /// Determine whether the module M is part of the current module from the /// perspective of a module-private visibility check. -static bool isInCurrentModule(const Module *M, const LangOptions &LangOpts) { +static bool isInCurrentModule(const Module *M, const Sema &SemaRef) { // If M is the global module fragment of a module that we've not yet finished // parsing, then it must be part of the current module. - return M->getTopLevelModuleName() == LangOpts.CurrentModule || + return M->getTopLevelPrimaryModuleName() == SemaRef.getCurrentModuleName() || (M->Kind == Module::GlobalModuleFragment && !M->Parent); } @@ -1574,7 +1574,7 @@ bool Sema::hasMergedDefinitionInCurrentModule(NamedDecl *Def) { for (const Module *Merged : Context.getModulesWithMergedDefinition(Def)) - if (isInCurrentModule(Merged, getLangOpts())) + if (isInCurrentModule(Merged, *this)) return true; return false; } @@ -1756,7 +1756,7 @@ // means it is part of the current module. For any other query, that means it // is in our visible module set. if (ModulePrivate) { - if (isInCurrentModule(M, getLangOpts())) + if (isInCurrentModule(M, *this)) return true; } else { if (VisibleModules.isVisible(M)) 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 @@ -89,7 +89,8 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, - ModuleDeclKind MDK, ModuleIdPath Path, bool IsFirstDecl) { + ModuleDeclKind MDK, ModuleIdPath Path, + ModuleIdPath PartitionPath, bool IsFirstDecl) { assert((getLangOpts().ModulesTS || getLangOpts().CPlusPlusModules) && "should only have module decl in Modules TS or C++20"); @@ -165,6 +166,16 @@ ModuleName += Piece.first->getName(); } + if (MDK == ModuleDeclKind::Partition) { + std::string PartitionName; + for (auto &Piece : PartitionPath) { + if (!PartitionName.empty()) + PartitionName += "."; + PartitionName += Piece.first->getName(); + } + ModuleName += "-" + PartitionName; + } + // If a module name was explicitly specified on the command line, it must be // correct. if (!getLangOpts().CurrentModule.empty() && @@ -180,6 +191,7 @@ Module *Mod; switch (MDK) { + case ModuleDeclKind::Partition: case ModuleDeclKind::Interface: { // We can't have parsed or imported a definition of this module or parsed a // module map defining it already. @@ -197,6 +209,7 @@ // Create a Module for the module that we're defining. Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName, GlobalModuleFragment); + assert(Mod && "module creation should not fail"); break; } @@ -272,6 +285,11 @@ return nullptr; } + if (IsCurrentModulePartition()) { + Diag(PrivateLoc, diag::err_private_module_fragment_in_module_partition); + return nullptr; + } + // FIXME: Check this isn't a module interface partition. // FIXME: Check that this translation unit does not import any partitions; // such imports would violate [basic.link]/2's "shall be the only module unit" @@ -306,17 +324,25 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, SourceLocation ExportLoc, - SourceLocation ImportLoc, - ModuleIdPath Path) { + SourceLocation ImportLoc, ModuleIdPath Path, + bool IsPartition) { // Flatten the module path for a Modules TS module name. std::pair ModuleNameLoc; - if (getLangOpts().ModulesTS) { + if (getLangOpts().ModulesTS || IsPartition) { std::string ModuleName; for (auto &Piece : Path) { if (!ModuleName.empty()) ModuleName += "."; ModuleName += Piece.first->getName(); } + + if (IsPartition) { + assert(getLangOpts().CPlusPlusModules && + "Partitions is only allowed in C++20 modules."); + if (getLangOpts().CurrentModule.empty()) + Diag(ImportLoc, diag::err_module_import_in_non_module); + ModuleName = std::string(getCurrentModuleName()) + "-" + ModuleName; + } ModuleNameLoc = {PP.getIdentifierInfo(ModuleName), Path[0].second}; Path = ModuleIdPath(ModuleNameLoc); } @@ -327,7 +353,8 @@ if (!Mod) return true; - return ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Mod, Path); + return ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Mod, Path, + IsPartition); } /// Determine whether \p D is lexically within an export-declaration. @@ -340,8 +367,8 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, SourceLocation ExportLoc, - SourceLocation ImportLoc, - Module *Mod, ModuleIdPath Path) { + SourceLocation ImportLoc, Module *Mod, + ModuleIdPath Path, bool IsPartition) { VisibleModules.setVisible(Mod, ImportLoc); checkModuleImportContext(*this, Mod, ImportLoc, CurContext); @@ -351,14 +378,13 @@ // silently ignoring the import. // Import-from-implementation is valid in the Modules TS. FIXME: Should we // warn on a redundant import of the current module? - // FIXME: Import of a module from an implementation partition of the same - // module is permitted. - if (Mod->getTopLevelModuleName() == getLangOpts().CurrentModule && - (getLangOpts().isCompilingModule() || !getLangOpts().ModulesTS)) { + if (Mod->getTopLevelPrimaryModuleName() == getCurrentModuleName() && + (getLangOpts().isCompilingModule() || !getLangOpts().ModulesTS) && + !IsPartition && !IsCurrentModulePartition()) { Diag(ImportLoc, getLangOpts().isCompilingModule() ? diag::err_module_self_import : diag::err_module_import_in_implementation) - << Mod->getFullModuleName() << getLangOpts().CurrentModule; + << Mod->getFullModuleName() << getCurrentModuleName(); } SmallVector IdentifierLocs; @@ -708,3 +734,13 @@ return D; } + +StringRef Sema::getCurrentModuleName() const { + if (!getLangOpts().CPlusPlusModules) + return getLangOpts().CurrentModule; + return StringRef(getLangOpts().CurrentModule).split('-').first; +} + +bool Sema::IsCurrentModulePartition() const { + return StringRef(getLangOpts().CurrentModule).contains('-'); +} diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -8654,6 +8654,8 @@ if (Diagnoser && SuggestedDef) diagnoseMissingImport(Loc, SuggestedDef, MissingImportKind::Definition, /*Recover*/TreatAsComplete); + // FIXME: It is possible to emit duplicated diagnostic message. See + // CXX/module/module.reach/p4/X.cpp for example. return !TreatAsComplete; } else if (Def && !TemplateInstCallbacks.empty()) { CodeSynthesisContext TempInst; diff --git a/clang/test/CXX/module/module.global.frag/Inputs/X-A.cppm b/clang/test/CXX/module/module.global.frag/Inputs/X-A.cppm new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.global.frag/Inputs/X-A.cppm @@ -0,0 +1,3 @@ +module; +#include "header.h" +export module X:A; diff --git a/clang/test/CXX/module/module.global.frag/Inputs/header.h b/clang/test/CXX/module/module.global.frag/Inputs/header.h new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.global.frag/Inputs/header.h @@ -0,0 +1 @@ +constexpr int n = 1; diff --git a/clang/test/CXX/module/module.global.frag/p2.cpp b/clang/test/CXX/module/module.global.frag/p2.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.global.frag/p2.cpp @@ -0,0 +1,13 @@ +// This test tests that the declaration in the global module fragment +// couldn't be used in other module unit even they are in the same module. +// RUN: rm -fr %t +// RUN: mkdir %t +// RUN: %clang_cc1 -std=c++20 %S/Inputs/X-A.cppm -emit-module-interface -o %t/X-A.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -verify %s + +export module X:B; +import :A; +export int foo() { + return n; // expected-error {{'n' must be declared before it is used}} + // expected-note@Inputs/header.h:1 {{declaration here is not visible}} +} diff --git a/clang/test/CXX/module/module.import/Inputs/M-Part.cppm b/clang/test/CXX/module/module.import/Inputs/M-Part.cppm new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.import/Inputs/M-Part.cppm @@ -0,0 +1 @@ +module M:Part; diff --git a/clang/test/CXX/module/module.import/p8.cpp b/clang/test/CXX/module/module.import/p8.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.import/p8.cpp @@ -0,0 +1,9 @@ +// RUN: rm -fr %t +// RUN: mkdir %t +// RUN: %clang_cc1 -std=c++20 %S/Inputs/M-Part.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/M-Part.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -verify %s +// expected-no-diagnostics +// FIXME: We couldn't export module M:Part since it is an implementation module. + +export module M; +export import :Part; // error: exported partition :Part is an implementation unit diff --git a/clang/test/CXX/module/module.private.frag/p1.cpp b/clang/test/CXX/module/module.private.frag/p1.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.private.frag/p1.cpp @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 -std=c++20 -verify %s + +export module X:A; +export struct S; +module :private; // expected-error {{private module fragment is not allowed to appear in a module partition}} +struct S{}; diff --git a/clang/test/CXX/module/module.reach/Inputs/p4/M-A.cppm b/clang/test/CXX/module/module.reach/Inputs/p4/M-A.cppm new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.reach/Inputs/p4/M-A.cppm @@ -0,0 +1,2 @@ +export module M:A; +export struct B; diff --git a/clang/test/CXX/module/module.reach/Inputs/p4/M-B.cppm b/clang/test/CXX/module/module.reach/Inputs/p4/M-B.cppm new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.reach/Inputs/p4/M-B.cppm @@ -0,0 +1,4 @@ +module M:B; +struct B { + operator int(); +}; diff --git a/clang/test/CXX/module/module.reach/Inputs/p4/M.cppm b/clang/test/CXX/module/module.reach/Inputs/p4/M.cppm new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.reach/Inputs/p4/M.cppm @@ -0,0 +1,5 @@ +export module M; +export import :A; +import :B; +B b2; +export void f(B b = B()); diff --git a/clang/test/CXX/module/module.reach/p4/M-C.cppm b/clang/test/CXX/module/module.reach/p4/M-C.cppm new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.reach/p4/M-C.cppm @@ -0,0 +1,10 @@ +// RUN: rm -fr %t +// RUN: mkdir %t +// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p4/M-A.cppm -emit-module-interface -o %t/M-A.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -verify %s + +module M:C; +import :A; // Since the compiler couldn't see the defintion of 'B' too, it couldn't + // give better diagnostic message. +B b1; // expected-error {{variable has incomplete type 'B'}} + // expected-note@../Inputs/p4/M-A.cppm:2 {{forward declaration of 'B'}} diff --git a/clang/test/CXX/module/module.reach/p4/X.cpp b/clang/test/CXX/module/module.reach/p4/X.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.reach/p4/X.cpp @@ -0,0 +1,15 @@ +// RUN: rm -fr %t +// RUN: mkdir %t +// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p4/M-A.cppm -emit-module-interface -o %t/M-A.pcm +// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p4/M-B.cppm -emit-module-interface -o %t/M-B.pcm +// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p4/M.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/M.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -verify %s + +export module X; +import M; + // FIXME: Eliminate the duplicated diagnostics. +B b3; // expected-error {{definition of 'B' must be imported}} + // expected-error@-1 {{definition of 'B' must be imported}} + // expected-note@../Inputs/p4/M-B.cppm:2 {{definition here is not reachable}} + // expected-note@../Inputs/p4/M-B.cppm:2 {{definition here is not reachable}} +void g() { f(); } // FIXME: The default argument of f() shouldn't be reachable from here. diff --git a/clang/test/CXX/module/module.unit/Inputs/p3/X.cppm b/clang/test/CXX/module/module.unit/Inputs/p3/X.cppm new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/Inputs/p3/X.cppm @@ -0,0 +1,6 @@ +export module X; +export import :parta; +import :partb; +export int use() { + return a() + b(); +} diff --git a/clang/test/CXX/module/module.unit/Inputs/p3/parta.cppm b/clang/test/CXX/module/module.unit/Inputs/p3/parta.cppm new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/Inputs/p3/parta.cppm @@ -0,0 +1,5 @@ +export module X:parta; + +export int a() { + return 1; +} diff --git a/clang/test/CXX/module/module.unit/Inputs/p3/partb.cppm b/clang/test/CXX/module/module.unit/Inputs/p3/partb.cppm new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/Inputs/p3/partb.cppm @@ -0,0 +1,5 @@ +module X:partb; +import :parta; +int b() { + return 2 + a(); +} diff --git a/clang/test/CXX/module/module.unit/Inputs/p4/A-Foo.cppm b/clang/test/CXX/module/module.unit/Inputs/p4/A-Foo.cppm new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/Inputs/p4/A-Foo.cppm @@ -0,0 +1,3 @@ +export module A:Foo; +import :Internals; +export int foo() { return 2 * (bar() + 1); } diff --git a/clang/test/CXX/module/module.unit/Inputs/p4/A-Impl.cpp b/clang/test/CXX/module/module.unit/Inputs/p4/A-Impl.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/Inputs/p4/A-Impl.cpp @@ -0,0 +1,5 @@ +// expected-no-diagnostics +module A; +import :Internals; +int bar() { return baz() - 10; } +int baz() { return 30; } diff --git a/clang/test/CXX/module/module.unit/Inputs/p4/A-Internals.cppm b/clang/test/CXX/module/module.unit/Inputs/p4/A-Internals.cppm new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/Inputs/p4/A-Internals.cppm @@ -0,0 +1,2 @@ +module A:Internals; +int bar(); diff --git a/clang/test/CXX/module/module.unit/Inputs/p4/A.cppm b/clang/test/CXX/module/module.unit/Inputs/p4/A.cppm new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/Inputs/p4/A.cppm @@ -0,0 +1,3 @@ +export module A; +export import :Foo; +export int baz(); diff --git a/clang/test/CXX/module/module.unit/Inputs/p8/B-X1.cppm b/clang/test/CXX/module/module.unit/Inputs/p8/B-X1.cppm new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/Inputs/p8/B-X1.cppm @@ -0,0 +1,2 @@ +module B:X1; // does not implicitly import B +int &a = n; // expected-error {{use of undeclared identifier 'n'}} diff --git a/clang/test/CXX/module/module.unit/Inputs/p8/B-X2.cppm b/clang/test/CXX/module/module.unit/Inputs/p8/B-X2.cppm new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/Inputs/p8/B-X2.cppm @@ -0,0 +1,4 @@ +// expected-no-diagnostics +module B:X2; // does not implicitly import B +import B; +int &b = n; // OK diff --git a/clang/test/CXX/module/module.unit/Inputs/p8/B-Y.cppm b/clang/test/CXX/module/module.unit/Inputs/p8/B-Y.cppm new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/Inputs/p8/B-Y.cppm @@ -0,0 +1,5 @@ +module B:Y; // does not implicitly import B +int y(); +#ifdef TESTING +int _ = n; // expected-error {{use of undeclared identifier}} +#endif diff --git a/clang/test/CXX/module/module.unit/Inputs/p8/B.cppm b/clang/test/CXX/module/module.unit/Inputs/p8/B.cppm new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/Inputs/p8/B.cppm @@ -0,0 +1,3 @@ +export module B; +import :Y; // OK, does not create interface dependency cycle +int n = y(); diff --git a/clang/test/CXX/module/module.unit/p3.cpp b/clang/test/CXX/module/module.unit/p3.cpp deleted file mode 100644 --- a/clang/test/CXX/module/module.unit/p3.cpp +++ /dev/null @@ -1,4 +0,0 @@ -// RUN: %clang_cc1 -std=c++2a -verify %s - -export module foo:bar; // expected-error {{sorry, module partitions are not yet supported}} -import :baz; // expected-error {{sorry, module partitions are not yet supported}} diff --git a/clang/test/CXX/module/module.unit/p3/FromNonModule.cpp b/clang/test/CXX/module/module.unit/p3/FromNonModule.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/p3/FromNonModule.cpp @@ -0,0 +1,11 @@ +// RUN: rm -fr %t +// RUN: mkdir %t +// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p3/parta.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/X-parta.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -verify %s + +import :parta; // expected-error {{import module partition isn't allowed in non module.}} + // expected-error@-1 {{not found}} + +int use() { + return a(); +} diff --git a/clang/test/CXX/module/module.unit/p3/FromWrongModule.cpp b/clang/test/CXX/module/module.unit/p3/FromWrongModule.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/p3/FromWrongModule.cpp @@ -0,0 +1,11 @@ +// RUN: rm -fr %t +// RUN: mkdir %t +// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p3/parta.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/Y-parta.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -verify %s + +export module Y; +import :parta; // expected-error {{error in loading module 'Y-parta' from prebuilt module path}} + +int use() { + return a(); +} diff --git a/clang/test/CXX/module/module.unit/p3/simple.cpp b/clang/test/CXX/module/module.unit/p3/simple.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/p3/simple.cpp @@ -0,0 +1,4 @@ +// RUN: %clang_cc1 -std=c++20 -verify %s +// expected-no-diagnostics + +export module foo:bar; diff --git a/clang/test/CXX/module/module.unit/p3/user.cpp b/clang/test/CXX/module/module.unit/p3/user.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/p3/user.cpp @@ -0,0 +1,13 @@ +// RUN: rm -fr %t +// RUN: mkdir %t +// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p3/parta.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/X-parta.pcm +// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p3/partb.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/X-partb.pcm +// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p3/X.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/X.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -verify %s +// expected-no-diagnostics + +import X; + +int user() { + return use(); +} diff --git a/clang/test/CXX/module/module.unit/p4/user.cpp b/clang/test/CXX/module/module.unit/p4/user.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/p4/user.cpp @@ -0,0 +1,19 @@ +// RUN: rm -fr %t +// RUN: mkdir %t +// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p4/A-Internals.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/A-Internals.pcm +// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p4/A-Foo.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/A-Foo.pcm +// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p4/A.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/A.pcm +// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p4/A-Impl.cpp -fmodule-file=%t/A.pcm -fprebuilt-module-path=%t -verify +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -verify %s + +import A; + +int user() { + return foo()+baz(); +} + +int user2() { + return foo() + baz() + bar(); // expected-error {{use of undeclared identifier 'bar'}} + // expected-note@*{{declared here}} + // NOTE: The note above notes the possible compiling error, which is irrevelant with module +} diff --git a/clang/test/CXX/module/module.unit/p8/B-Impl.cppm b/clang/test/CXX/module/module.unit/p8/B-Impl.cppm new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/p8/B-Impl.cppm @@ -0,0 +1,12 @@ +// RUN: rm -fr %t +// RUN: mkdir %t +// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p8/B-Y.cppm -fprebuilt-module-path=%t -DTESTING -verify +// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p8/B-Y.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/B-Y.pcm +// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p8/B.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/B.pcm +// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p8/B-X1.cppm -fprebuilt-module-path=%t -verify +// RUN: %clang_cc1 -std=c++20 %S/../Inputs/p8/B-X2.cppm -fprebuilt-module-path=%t -verify +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -fmodule-file=%t/B.pcm -verify %s +// expected-no-diagnostics + +module B; // implicitly imports B +int &c = n; // OK diff --git a/clang/test/CXX/module/module.unit/p8.cpp b/clang/test/CXX/module/module.unit/p8/ImplicitImport.cpp rename from clang/test/CXX/module/module.unit/p8.cpp rename to clang/test/CXX/module/module.unit/p8/ImplicitImport.cpp --- a/clang/test/CXX/module/module.unit/p8.cpp +++ b/clang/test/CXX/module/module.unit/p8/ImplicitImport.cpp @@ -24,12 +24,10 @@ export module bar; #elif MODE == 4 -module foo:bar; // expected-error {{not yet supported}} -#define IMPORTED // FIXME +module foo:bar; #elif MODE == 5 -export module foo:bar; // expected-error {{not yet supported}} expected-error {{redefinition}} expected-note@* {{loaded from}} -#define IMPORTED // FIXME +export module foo:bar; #endif