iains created this revision. Herald added a project: All. iains added reviewers: urnathan, ChuanqiXu. iains added a subscriber: clang-modules. iains published this revision for review. Herald added a project: clang. Herald added a subscriber: cfe-commits.
This adds a check for exported inline functions, that there is a definition in the definition domain (which, in practice, can only be the module purview but before any PMF starts) since the PMF definition domain cannot contain exports. This is: [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. The patch also amends diagnostic output by excluding the PMF sub-module from the set considered as sources of missing decls. There is no point in telling the user that the import of a PMF object is missing - since such objects are never reachable to an importer. We still show the definition (as unreachable), to help point out this. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D128328 Files: clang/include/clang/Basic/DiagnosticSemaKinds.td clang/include/clang/Sema/Sema.h clang/lib/Sema/Sema.cpp clang/lib/Sema/SemaLookup.cpp clang/lib/Sema/SemaModule.cpp clang/test/Modules/cxx20-10-5-ex1.cpp
Index: clang/test/Modules/cxx20-10-5-ex1.cpp =================================================================== --- /dev/null +++ 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-n...@std-10-5-ex1-interface.cpp:19 1+{{definition here is not reachable}} + X *p = factory(); +} Index: clang/lib/Sema/SemaModule.cpp =================================================================== --- clang/lib/Sema/SemaModule.cpp +++ clang/lib/Sema/SemaModule.cpp @@ -892,6 +892,17 @@ diagExportedUnnamedDecl(*this, UnnamedDeclKind::Context, Child, BlockStart); } + if (auto *FD = dyn_cast<FunctionDecl>(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); + } } } Index: clang/lib/Sema/SemaLookup.cpp =================================================================== --- clang/lib/Sema/SemaLookup.cpp +++ clang/lib/Sema/SemaLookup.cpp @@ -5425,7 +5425,7 @@ llvm::SmallVector<Module*, 8> UniqueModules; llvm::SmallDenseSet<Module*, 8> 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); Index: clang/lib/Sema/Sema.cpp =================================================================== --- clang/lib/Sema/Sema.cpp +++ 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<FunctionDecl>(D)) { + if (!FD->isDefined()) + Diag(FD->getLocation(), diag::err_export_inline_not_defined); + } + PendingInlineExports.clear(); + } + emitDeferredDiags(); assert(LateParsedInstantiations.empty() && Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -2245,6 +2245,10 @@ /// Namespace definitions that we will export when they finish. llvm::SmallPtrSet<const NamespaceDecl*, 8> 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<const Decl *, 8> PendingInlineExports; + /// Get the module unit whose scope we are currently within. Module *getCurrentModule() const { return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module; Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ 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<
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits