sepavloff created this revision.
sepavloff added a subscriber: cfe-commits.

Instantiation of static class members can be not obvious in some cases. Using
modules can cause problems even more difficult to diagnose. PR24425 describes
one of such cases. As a way to assist a user, compiler could issue warnings if
code that is potentially erroneous. This patch implements a warning if a static
class member is used in a module, but cannot be instantiated there.

http://reviews.llvm.org/D12326

Files:
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  lib/Sema/SemaDecl.cpp
  test/Modules/Inputs/module-inst.cpp
  test/Modules/Inputs/module-inst/a.h
  test/Modules/Inputs/module-inst/module.modulemap

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"
+}
Index: test/Modules/Inputs/module-inst/a.h
===================================================================
--- /dev/null
+++ test/Modules/Inputs/module-inst/a.h
@@ -0,0 +1,9 @@
+template <class T> struct Foo_0 {
+  static char s_bar_0;
+};
+template <class T> struct Foo {
+  static char s_bar; // expected-warning{{specialization of the template must be provided somewhere in the application}}
+};
+char baz() {
+  return Foo<int>::s_bar;
+}
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 <typename T> char Foo<T>::s_bar;
+
+// CHECK: 1 warning generated.
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 <algorithm>
@@ -14321,6 +14323,44 @@
   }
 }
 
+namespace {
+
+/// \brief Visitor that processes declarations that are instantiated from
+/// templates.
+///
+class InstantiationScanner : public RecursiveASTVisitor<InstantiationScanner> {
+  Sema &SemaRef;
+  llvm::SmallPtrSet<ValueDecl*, 16> 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<VarDecl>(D)) {
+      if (VD->getDeclContext()->isRecord()) {
+        if (isa<ClassTemplateSpecializationDecl>(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: 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<ModuleInstantiate>;
 }
 
 let CategoryName = "Documentation Issue" in {
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">;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to