Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -247,6 +247,7 @@ def MissingFieldInitializers : DiagGroup<"missing-field-initializers">; def ModuleBuild : DiagGroup<"module-build">; def ModuleConflict : DiagGroup<"module-conflict">; +def ModuleInstantiate : DiagGroup<"module-instantiate">; def NewlineEOF : DiagGroup<"newline-eof">; def Nullability : DiagGroup<"nullability">; def NullabilityDeclSpec : DiagGroup<"nullability-declspec">; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -7741,6 +7741,9 @@ "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 warn_module_external_spec : Warning< + "specialization of the template must be provided somewhere in the application">, + InGroup; } let CategoryName = "Documentation Issue" in { Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -24,6 +24,7 @@ #include "clang/AST/DeclTemplate.h" #include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/StmtCXX.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/PartialDiagnostic.h" @@ -43,6 +44,7 @@ #include "clang/Sema/Scope.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/Template.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/Triple.h" #include @@ -14321,6 +14323,44 @@ } } +namespace { + +/// \brief Visitor that processes declarations that are instantiated from +/// templates. +/// +class InstantiationScanner : public RecursiveASTVisitor { + Sema &SemaRef; + llvm::SmallPtrSet seen; +public: + InstantiationScanner(Sema &S) : SemaRef(S) { } + + bool VisitDeclRefExpr(DeclRefExpr *E) { + ValueDecl *D = E->getDecl(); + if (seen.count(D)) + return true; + if (VarDecl *VD = dyn_cast(D)) { + if (VD->getDeclContext()->isRecord()) { + if (isa(VD->getDeclContext())) { + // Reference to static class member instantiation + seen.insert(D); + bool HasDefinition = false; + for (auto *R : VD->redecls()) { + if (R->getDefinition()) { + HasDefinition = true; + break; + } + } + if (!HasDefinition) + SemaRef.Diag(VD->getLocation(), diag::warn_module_external_spec); + } + } + } + return true; + } +}; + +} + DeclResult Sema::ActOnModuleImport(SourceLocation AtLoc, SourceLocation ImportLoc, ModuleIdPath Path) { @@ -14401,6 +14441,8 @@ void Sema::ActOnModuleEnd(SourceLocation DirectiveLoc, Module *Mod) { checkModuleImportContext(*this, Mod, DirectiveLoc, CurContext); + InstantiationScanner Scanner(*this); + Scanner.TraverseDecl(getASTContext().getTranslationUnitDecl()); if (getLangOpts().ModulesLocalVisibility) { VisibleModules = std::move(VisibleModulesStack.back()); Index: test/Modules/Inputs/module-inst.cpp =================================================================== --- /dev/null +++ test/Modules/Inputs/module-inst.cpp @@ -0,0 +1,10 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I%S/Inputs/module-inst %s | FileCheck %s + +#include "a.h" +// CHECK: warning: specialization of the template must be provided somewhere in the application +// CHECK: static char s_bar; + +template char Foo::s_bar; + +// CHECK: 1 warning generated. Index: test/Modules/Inputs/module-inst/a.h =================================================================== --- /dev/null +++ test/Modules/Inputs/module-inst/a.h @@ -0,0 +1,9 @@ +template struct Foo_0 { + static char s_bar_0; +}; +template struct Foo { + static char s_bar; // expected-warning{{specialization of the template must be provided somewhere in the application}} +}; +char baz() { + return Foo::s_bar; +} Index: test/Modules/Inputs/module-inst/module.modulemap =================================================================== --- /dev/null +++ test/Modules/Inputs/module-inst/module.modulemap @@ -0,0 +1,3 @@ +module a { + header "a.h" +}