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 @@ -11151,6 +11151,9 @@ def err_export_not_in_module_interface : Error< "export declaration can only be used within a module interface unit" "%select{ after the module declaration|}0">; +def err_export_inline_not_defined : Error< + "exported inline functions must be defined within the module purview" + " and before any private module fragment">; def err_export_partition_impl : Error< "module partition implementations cannot be exported">; def err_export_in_private_module_fragment : Error< 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 @@ -2245,6 +2245,10 @@ /// Namespace definitions that we will export when they finish. llvm::SmallPtrSet DeferredExportedNamespaces; + /// Exported inlines that require a definition to be present at the end of + /// the TU (or before the PMF starts, if that is present). + llvm::SmallPtrSet PendingInlineExports; + /// Get the module unit whose scope we are currently within. Module *getCurrentModule() const { return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module; diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1095,6 +1095,15 @@ PerformPendingInstantiations(); } + if (Kind == TUFragmentKind::Normal && !PendingInlineExports.empty()) { + for (auto *D : PendingInlineExports) + if (auto *FD = dyn_cast(D)) { + if (!FD->isDefined()) + Diag(FD->getLocation(), diag::err_export_inline_not_defined); + } + PendingInlineExports.clear(); + } + emitDeferredDiags(); assert(LateParsedInstantiations.empty() && 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 @@ -5425,7 +5425,7 @@ llvm::SmallVector UniqueModules; llvm::SmallDenseSet UniqueModuleSet; for (auto *M : Modules) { - if (M->Kind == Module::GlobalModuleFragment) + if (M->isGlobalModule() || M->isPrivateModule()) continue; if (UniqueModuleSet.insert(M).second) UniqueModules.push_back(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 @@ -892,6 +892,17 @@ diagExportedUnnamedDecl(*this, UnnamedDeclKind::Context, Child, BlockStart); } + if (auto *FD = dyn_cast(Child)) { + // [dcl.inline]/7 + // If an inline function or variable that is attached to a named module + // is declared in a definition domain, it shall be defined in that + // domain. + // So, if the current declaration does not have a definition, we must + // check at the end of the TU (or when the PMF starts) to see that we + // have a definition at that point. + if (FD->isInlineSpecified() && !FD->isDefined()) + PendingInlineExports.insert(FD); + } } } diff --git a/clang/test/Modules/cxx20-10-5-ex1.cpp b/clang/test/Modules/cxx20-10-5-ex1.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/cxx20-10-5-ex1.cpp @@ -0,0 +1,50 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: cd %t + +// RUN: %clang_cc1 -std=c++20 -emit-module-interface std-10-5-ex1-interface.cpp \ +// RUN: -DBAD_FWD_DECL -fsyntax-only -verify + +// RUN: %clang_cc1 -std=c++20 -emit-module-interface std-10-5-ex1-interface.cpp \ +// RUN: -o A.pcm + +// RUN: %clang_cc1 -std=c++20 std-10-5-ex1-use.cpp -fmodule-file=A.pcm \ +// RUN: -fsyntax-only -verify + +//--- std-10-5-ex1-interface.cpp + +export module A; +#ifdef BAD_FWD_DECL +export inline void fn_e(); // expected-error {{exported inline functions must be defined within the module purview and before any private module fragment}} +#endif +export inline void ok_fn() {} +export inline void ok_fn2(); +inline void fn_m(); +static void fn_s(); +export struct X; +export void g(X *x) { + fn_s(); + fn_m(); +} +export X *factory(); +void ok_fn2() {} + +module :private; +struct X {}; +X *factory() { + return new X(); +} + +void fn_e() {} +void fn_m() {} +void fn_s() {} + +//--- std-10-5-ex1-use.cpp + +import A; + +void foo() { + X x; // expected-error 1+{{missing '#include'; 'X' must be defined before it is used}} + // expected-note@std-10-5-ex1-interface.cpp:19 1+{{definition here is not reachable}} + X *p = factory(); +}