Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10839,6 +10839,10 @@ "redefinition of module '%0'">; def note_prev_module_definition : Note<"previously defined here">; def note_prev_module_definition_from_ast_file : Note<"module loaded from '%0'">; +def err_module_langugae_linkage_no_global : Error < + "The declaration %0 appears within a linkage-specification should be " + "attached to global module. But the compiler failed to search the global " + "module.">; def err_module_not_defined : Error< "definition of module '%0' is not available; use -fmodule-file= to specify " "path to precompiled module interface">; Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -2188,6 +2188,7 @@ }; /// The modules we're currently parsing. llvm::SmallVector ModuleScopes; + Module *GlobalModule = nullptr; /// Namespace definitions that we will export when they finish. llvm::SmallPtrSet DeferredExportedNamespaces; @@ -2197,6 +2198,8 @@ return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module; } + Module *getGlobalModule() const { return GlobalModule; } + VisibleModuleSet VisibleModules; public: Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -9339,6 +9339,23 @@ } } + // C++ [module.unit]p7.2 + // Otherwise, if the declaration + // - ... + // - ... + // - appears within a linkage-specification + // it is attached to the global module. + if (Module *M = NewFD->getOwningModule()) + if (M->Kind == Module::ModuleInterfaceUnit && + (NewFD->isExternCContext() || NewFD->isExternCXXContext())) { + if (!getGlobalModule()) + Diag(NewFD->getLocation(), + diag::err_module_langugae_linkage_no_global) + << NewFD->getName(); + NewFD->setLocalOwningModule(getGlobalModule()); + NewFD->setModuleOwnershipKind(Decl::ModuleOwnershipKind::Unowned); + } + if (isFriend) { if (FunctionTemplate) { FunctionTemplate->setObjectOfFriendDecl(); @@ -16407,6 +16424,22 @@ New->setModulePrivate(); } + // C++ [module.unit]p7.2 + // Otherwise, if the declaration + // - ... + // - ... + // - appears within a linkage-specification + // it is attached to the global module. + if (Module *M = New->getOwningModule()) + if (M->Kind == Module::ModuleInterfaceUnit && + (New->isExternCContext() || New->isExternCXXContext())) { + if (!getGlobalModule()) + Diag(New->getLocation(), diag::err_module_langugae_linkage_no_global) + << New->getName(); + New->setLocalOwningModule(getGlobalModule()); + New->setModuleOwnershipKind(Decl::ModuleOwnershipKind::Unowned); + } + // If this is a specialization of a member class (of a class template), // check the specialization. if (isMemberSpecialization && CheckMemberSpecialization(New, Previous)) Index: clang/lib/Sema/SemaModule.cpp =================================================================== --- clang/lib/Sema/SemaModule.cpp +++ clang/lib/Sema/SemaModule.cpp @@ -66,10 +66,13 @@ return nullptr; } + assert( + !GlobalModule && + "Global module already created but didn't covered by the check above."); // 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); + GlobalModule = Map.createGlobalModuleFragmentForModuleUnit(ModuleLoc); assert(GlobalModule && "module creation should not fail"); // Enter the scope of the global module. Index: clang/test/CXX/module/module.linkage_specification/Inputs/h1.h =================================================================== --- /dev/null +++ clang/test/CXX/module/module.linkage_specification/Inputs/h1.h @@ -0,0 +1,6 @@ +extern "C" void foo(); +extern "C" { + void bar(); + int baz(); + double double_func(); +} \ No newline at end of file Index: clang/test/CXX/module/module.linkage_specification/Inputs/h2.h =================================================================== --- /dev/null +++ clang/test/CXX/module/module.linkage_specification/Inputs/h2.h @@ -0,0 +1 @@ +extern "C++" class CPP; \ No newline at end of file Index: clang/test/CXX/module/module.linkage_specification/Inputs/h4.h =================================================================== --- /dev/null +++ clang/test/CXX/module/module.linkage_specification/Inputs/h4.h @@ -0,0 +1 @@ +extern "C" struct C; \ No newline at end of file Index: clang/test/CXX/module/module.linkage_specification/p1.cpp =================================================================== --- /dev/null +++ clang/test/CXX/module/module.linkage_specification/p1.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -std=c++2a %s -verify +// expected-no-diagnostics +module; + +#include "Inputs/h1.h" + +export module x; + +extern "C" void foo() { + return; +} Index: clang/test/CXX/module/module.linkage_specification/p2.cpp =================================================================== --- /dev/null +++ clang/test/CXX/module/module.linkage_specification/p2.cpp @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -std=c++2a %s -verify +// expected-no-diagnostics +module; + +#include "Inputs/h2.h" + +export module x; + +extern "C++" class CPP {}; Index: clang/test/CXX/module/module.linkage_specification/p3.cpp =================================================================== --- /dev/null +++ clang/test/CXX/module/module.linkage_specification/p3.cpp @@ -0,0 +1,5 @@ +// RUN: %clang_cc1 -std=c++2a %s -verify +export module x; + +extern "C" void foo(); // expected-error {{The declaration foo appears within a linkage-specification should be attached to global module. But the compiler failed to search the global module.}} +extern "C++" class CPP {}; // expected-error {{The declaration CPP appears within a linkage-specification should be attached to global module. But the compiler failed to search the global module.}} Index: clang/test/CXX/module/module.linkage_specification/p4.cpp =================================================================== --- /dev/null +++ clang/test/CXX/module/module.linkage_specification/p4.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -std=c++2a %s -verify +// expected-no-diagnostics +module; + +#include "Inputs/h4.h" + +export module x; + +extern "C" struct C { + int a; + int b; + double d; +}; \ No newline at end of file Index: clang/test/CodeGenCXX/Inputs/module-extern-C.h =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/Inputs/module-extern-C.h @@ -0,0 +1 @@ +extern "C" void foo(); \ No newline at end of file Index: clang/test/CodeGenCXX/module-extern-C.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/module-extern-C.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -std=c++2a -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; + } +} \ No newline at end of file