]> granicus.if.org Git - clang/commitdiff
Warn about code that uses variables and functions with internal linkage
authorJohn McCall <rjmccall@apple.com>
Sat, 19 Feb 2011 02:53:41 +0000 (02:53 +0000)
committerJohn McCall <rjmccall@apple.com>
Sat, 19 Feb 2011 02:53:41 +0000 (02:53 +0000)
without defining them.  This should be an error, but I'm paranoid about
"uses" that end up not actually requiring a definition.  I'll revisit later.

Also, teach IR generation to not set internal linkage on variable
declarations, just for safety's sake.  Doing so produces an invalid module
if the variable is not ultimately defined.

Also, fix several places in the test suite where we were using internal
functions without definitions.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@126016 91177308-0d34-0410-b5e6-96231b3b80d8

16 files changed:
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/Sema.h
lib/AST/Decl.cpp
lib/CodeGen/CodeGenModule.cpp
lib/Sema/Sema.cpp
lib/Sema/SemaExpr.cpp
test/Analysis/cxx-crashes.cpp
test/Analysis/misc-ps-64.m
test/CodeGen/regparm.c
test/CodeGen/sizeof-vla.c
test/CodeGenCXX/constructor-convert.cpp
test/CodeGenCXX/internal-linkage.cpp
test/CodeGenCXX/key-function-vtable.cpp
test/CodeGenCXX/thunks.cpp
test/Rewriter/properties.m
test/SemaCXX/undefined-internal.cpp [new file with mode: 0644]

index d38d14c8565b074f1883cf10bc8127647eb157b5..4560f6e9a34cf9e439189b2e1e88e9b79f83d904 100644 (file)
@@ -1997,6 +1997,11 @@ def err_definition_of_implicitly_declared_member : Error<
 def err_redefinition_extern_inline : Error<
   "redefinition of a 'extern inline' function %0 is not supported in "
   "%select{C99 mode|C++}1">;
+
+// This should eventually be an error.
+def warn_undefined_internal : Warning<
+  "%select{function|variable}0 %q1 has internal linkage but is not defined">;
+def note_used_here : Note<"used here">;
   
 def warn_redefinition_of_typedef : Warning<
   "redefinition of typedef %0 is invalid in C">,
index 4e3173ab09cdb5aa6ff1c8d5f98339468b13ca9e..a835bb219e3fc0e1f88a56fe7fed03505bc819eb 100644 (file)
@@ -600,6 +600,10 @@ public:
   // argument locations.
   llvm::DenseMap<ParmVarDecl *,SourceLocation> UnparsedDefaultArgLocs;
 
+  /// UndefinedInternals - all the used, undefined objects with
+  /// internal linkage in this translation unit.
+  llvm::DenseMap<NamedDecl*, SourceLocation> UndefinedInternals;
+
   typedef std::pair<ObjCMethodList, ObjCMethodList> GlobalMethods;
   typedef llvm::DenseMap<Selector, GlobalMethods> GlobalMethodPool;
 
index d5e9dbf4fc7ded0ab661ef6479700eedb2f3c47b..7d4b461f5a7c87d718cbb7939a1bbe7b0e973406 100644 (file)
@@ -637,14 +637,26 @@ void NamedDecl::ClearLinkageCache() {
   if (const CXXRecordDecl *record = dyn_cast<CXXRecordDecl>(this))
     clearLinkageForClass(record);
 
-  if (const ClassTemplateDecl *temp = dyn_cast<ClassTemplateDecl>(this)) {
+  if (ClassTemplateDecl *temp =
+        dyn_cast<ClassTemplateDecl>(const_cast<NamedDecl*>(this))) {
     // Clear linkage for the template pattern.
     CXXRecordDecl *record = temp->getTemplatedDecl();
     record->HasCachedLinkage = 0;
     clearLinkageForClass(record);
 
-    // ...do we need to clear linkage for specializations, too?
+    // We need to clear linkage for specializations, too.
+    for (ClassTemplateDecl::spec_iterator
+           i = temp->spec_begin(), e = temp->spec_end(); i != e; ++i)
+      i->ClearLinkageCache();
   }
+
+  // Clear cached linkage for function template decls, too.
+  if (FunctionTemplateDecl *temp =
+        dyn_cast<FunctionTemplateDecl>(const_cast<NamedDecl*>(this)))
+    for (FunctionTemplateDecl::spec_iterator
+           i = temp->spec_begin(), e = temp->spec_end(); i != e; ++i)
+      i->ClearLinkageCache();
+    
 }
 
 Linkage NamedDecl::getLinkage() const {
index 5a45df24fe8f1ae737d9291cf423bf1ee8cbd661..9e5d7cf11276ffa478e129ddeef3e580570de223 100644 (file)
@@ -983,7 +983,7 @@ CodeGenModule::GetOrCreateLLVMGlobal(llvm::StringRef MangledName,
     // Set linkage and visibility in case we never see a definition.
     NamedDecl::LinkageInfo LV = D->getLinkageAndVisibility();
     if (LV.linkage() != ExternalLinkage) {
-      GV->setLinkage(llvm::GlobalValue::InternalLinkage);
+      // Don't set internal linkage on declarations.
     } else {
       if (D->hasAttr<DLLImportAttr>())
         GV->setLinkage(llvm::GlobalValue::DLLImportLinkage);
index 7eb1c57412c85ee2107a94227239c849345d3873..23a3c24804e6502de242c9cc6563bf8467cfe3b0 100644 (file)
@@ -271,6 +271,65 @@ static bool ShouldRemoveFromUnused(Sema *SemaRef, const DeclaratorDecl *D) {
   return false;
 }
 
+namespace {
+  struct UndefinedInternal {
+    NamedDecl *decl;
+    FullSourceLoc useLoc;
+
+    UndefinedInternal(NamedDecl *decl, FullSourceLoc useLoc)
+      : decl(decl), useLoc(useLoc) {}
+  };
+
+  bool operator<(const UndefinedInternal &l, const UndefinedInternal &r) {
+    return l.useLoc.isBeforeInTranslationUnitThan(r.useLoc);
+  }
+}
+
+/// checkUndefinedInternals - Check for undefined objects with internal linkage.
+static void checkUndefinedInternals(Sema &S) {
+  if (S.UndefinedInternals.empty()) return;
+
+  // Collect all the still-undefined entities with internal linkage.
+  llvm::SmallVector<UndefinedInternal, 16> undefined;
+  for (llvm::DenseMap<NamedDecl*,SourceLocation>::iterator
+         i = S.UndefinedInternals.begin(), e = S.UndefinedInternals.end();
+       i != e; ++i) {
+    NamedDecl *decl = i->first;
+
+    // Ignore attributes that have become invalid.
+    if (decl->isInvalidDecl()) continue;
+
+    // __attribute__((weakref)) is basically a definition.
+    if (decl->hasAttr<WeakRefAttr>()) continue;
+
+    if (FunctionDecl *fn = dyn_cast<FunctionDecl>(decl)) {
+      if (fn->isPure() || fn->hasBody())
+        continue;
+    } else {
+      if (cast<VarDecl>(decl)->hasDefinition() != VarDecl::DeclarationOnly)
+        continue;
+    }
+
+    // We build a FullSourceLoc so that we can sort with array_pod_sort.
+    FullSourceLoc loc(i->second, S.Context.getSourceManager());
+    undefined.push_back(UndefinedInternal(decl, loc));
+  }
+
+  if (undefined.empty()) return;
+
+  // Sort (in order of use site) so that we're not (as) dependent on
+  // the iteration order through an llvm::DenseMap.
+  llvm::array_pod_sort(undefined.begin(), undefined.end());
+
+  for (llvm::SmallVectorImpl<UndefinedInternal>::iterator
+         i = undefined.begin(), e = undefined.end(); i != e; ++i) {
+    NamedDecl *decl = i->decl;
+    S.Diag(decl->getLocation(), diag::warn_undefined_internal)
+      << isa<VarDecl>(decl) << decl;
+    S.Diag(i->useLoc, diag::note_used_here);
+  }
+}
+
 /// ActOnEndOfTranslationUnit - This is called at the very end of the
 /// translation unit when EOF is reached and all but the top-level scope is
 /// popped.
@@ -403,6 +462,8 @@ void Sema::ActOnEndOfTranslationUnit() {
               << DiagD->getDeclName();
       }
     }
+
+    checkUndefinedInternals(*this);
   }
 
   TUScope = 0;
index 792eb8af98b0aa20d5f1f78eaab99456039e947e..df49ad5c9a5f2ea22bcfa5a7707f8ed89cbe2c81 100644 (file)
@@ -9284,6 +9284,9 @@ void Sema::MarkDeclarationReferenced(SourceLocation Loc, Decl *D) {
       MarkVTableUsed(Loc, MethodDecl->getParent());
   }
   if (FunctionDecl *Function = dyn_cast<FunctionDecl>(D)) {
+    // Recursive functions should be marked when used from another function.
+    if (CurContext == Function) return;
+
     // Implicit instantiation of function templates and member functions of
     // class templates.
     if (Function->isImplicitlyInstantiable()) {
@@ -9312,19 +9315,23 @@ void Sema::MarkDeclarationReferenced(SourceLocation Loc, Decl *D) {
         else
           PendingInstantiations.push_back(std::make_pair(Function, Loc));
       }
-    } else // Walk redefinitions, as some of them may be instantiable.
+    } else {
+      // Walk redefinitions, as some of them may be instantiable.
       for (FunctionDecl::redecl_iterator i(Function->redecls_begin()),
            e(Function->redecls_end()); i != e; ++i) {
         if (!i->isUsed(false) && i->isImplicitlyInstantiable())
           MarkDeclarationReferenced(Loc, *i);
       }
+    }
 
-    // FIXME: keep track of references to static functions
-
-    // Recursive functions should be marked when used from another function.
-    if (CurContext != Function)
-      Function->setUsed(true);
+    // Keep track of used but undefined functions.
+    if (!Function->isPure() && !Function->hasBody() &&
+        Function->getLinkage() != ExternalLinkage) {
+      SourceLocation &old = UndefinedInternals[Function->getCanonicalDecl()];
+      if (old.isInvalid()) old = Loc;
+    }
 
+    Function->setUsed(true);
     return;
   }
 
@@ -9341,7 +9348,12 @@ void Sema::MarkDeclarationReferenced(SourceLocation Loc, Decl *D) {
       }
     }
 
-    // FIXME: keep track of references to static data?
+    // Keep track of used but undefined variables.
+    if (Var->hasDefinition() == VarDecl::DeclarationOnly
+        && Var->getLinkage() != ExternalLinkage) {
+      SourceLocation &old = UndefinedInternals[Var->getCanonicalDecl()];
+      if (old.isInvalid()) old = Loc;
+    }
 
     D->setUsed(true);
     return;
index cebc55bd4239792b541c3a139361e7d9c75c5386..ae2f3cb5ebe516191f1c64dc95477b62d6f3036c 100644 (file)
@@ -18,7 +18,7 @@ namespace {
 
 struct A { };
 struct B {
-  operator A();
+  operator A() { return A(); }
 };
 
 A f(char *dst) {
index 0dbd6cb948b38d997ac638e0e5ada43b8aecdcf2..f65673eea24fa65af2e899a98e8d12f1df68f1dd 100644 (file)
@@ -14,7 +14,7 @@ typedef unsigned char Boolean;
 typedef const struct __CFDictionary * CFDictionaryRef;
 
 extern Boolean CFDictionaryGetValueIfPresent(CFDictionaryRef theDict, const void *key, const void **value);
-static void shazam(NSUInteger i, unsigned char **out);
+void shazam(NSUInteger i, unsigned char **out);
 
 void rdar_6440393_1(NSDictionary *dict) {
   NSInteger x = 0;
index dd0be968185ee46e6acb495daec69184e7f16edd..d628b685f94a0b31c018e94a6b68aeca7c753834 100644 (file)
@@ -11,8 +11,7 @@ typedef struct {
 typedef void (*FType)(int, int)      __attribute ((regparm (3), stdcall));
 FType bar;
 
-static void FASTCALL
-reduced(char b, double c, foo* d, double e, int f);
+extern void FASTCALL reduced(char b, double c, foo* d, double e, int f);
 
 // PR7025
 void FASTCALL f1(int i, int j, int k);
index b0c514fd0161c66c840d2f0b59384a69f15391e7..c5fc91251933386655c766713e9199ba7b085bc4 100644 (file)
@@ -2,7 +2,7 @@
 
 // PR3442
 
-static void *g(unsigned long len);
+void *g(unsigned long len);
 
 void
 f(int n)
index 338febbe97a9a67d9f63523d9356e53b68e2a486..9122dae128ecaa8b8be6ec8f7a6d0b78c57b5dd7 100644 (file)
@@ -5,7 +5,7 @@ class Twine {
   Twine(const char *Str) { }
 };
 
-static void error(const Twine &Message);
+static void error(const Twine &Message) {}
 
 template<typename>
 struct opt_storage {
index 9fdb7274e146964dd05d2bea964ca5b7ffbe2b80..39bce8545ff5d89a87bdff94d7ac7ea0ff4826e8 100644 (file)
@@ -1,11 +1,11 @@
 // RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
 
 struct Global { Global(); };
-template<typename T> struct X { X(); };
+template<typename T> struct X { X() {} };
 
 
 namespace {
-  struct Anon { Anon(); };
+  struct Anon { Anon() {} };
 
   // CHECK: @_ZN12_GLOBAL__N_15anon0E = internal global
   Global anon0;
index 8378bbd2340e7d6c2452ca3001f4027b0382b152..8e474bdf95f11fcb307a5b5ef5d45e9c63eeecb1 100644 (file)
@@ -30,6 +30,7 @@ void testf::a() {}
 namespace {
   struct testg { virtual void a(); };
 }
+void testg::a() {}
 testg *testgvar = new testg;
 
 struct X0 { virtual ~X0(); };
index 238032cc6de24ba68bcd4d47a9d09bc60fc12482..a74cc053dbe528e3da296925797be78489670d23 100644 (file)
@@ -88,31 +88,29 @@ void C::f() { }
 }
 
 // Check that the thunk gets internal linkage.
-namespace {
-
-struct A {
-  virtual void f();
-};
-
-struct B {
-  virtual void f();
-};
-
-struct C : A, B {
-  virtual void c();
-
-  virtual void f();
-};
+namespace Test4B {
+  struct A {
+    virtual void f();
+  };
 
-void C::f() { }
+  struct B {
+    virtual void f();
+  };
 
-}
+  namespace {
+    struct C : A, B {
+      virtual void c();
+      virtual void f();
+    };
+  }
+  void C::c() {}
+  void C::f() {}
 
-// Force C::f to be used.
-void f() { 
-  C c; 
-  
-  c.f();
+  // Force C::f to be used.
+  void f() { 
+    C c; 
+    c.f();
+  }
 }
 
 namespace Test5 {
@@ -283,4 +281,4 @@ namespace Test11 {
 
 // This is from Test5:
 // CHECK: define linkonce_odr void @_ZTv0_n24_N5Test51B1fEv
-// CHECK: define internal void @_ZThn8_N12_GLOBAL__N_11C1fEv(
+// CHECK: define internal void @_ZThn8_N6Test4B12_GLOBAL__N_11C1fEv(
index 89177d7e64b5c4cd9426e3ea800a702fd94476e3..ca4a199cc64f8b3ac58687de725279f7d91bdad0 100644 (file)
@@ -38,7 +38,7 @@ void *sel_registerName(const char *);
 
 @implementation Bar
 
-static int func(int i);
+static int func(int i) { return 0; }
 
 - (void)baz {
     Foo *obj1, *obj2;
diff --git a/test/SemaCXX/undefined-internal.cpp b/test/SemaCXX/undefined-internal.cpp
new file mode 100644 (file)
index 0000000..bb87ce0
--- /dev/null
@@ -0,0 +1,86 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+// Make sure we don't produce invalid IR.
+// RUN: %clang_cc1 -emit-llvm-only %s
+
+namespace test1 {
+  static void foo(); // expected-warning {{function 'test1::foo' has internal linkage but is not defined}}
+  template <class T> static void bar(); // expected-warning {{function 'test1::bar<int>' has internal linkage but is not defined}}
+
+  void test() {
+    foo(); // expected-note {{used here}}
+    bar<int>(); // expected-note {{used here}}
+  }
+}
+
+namespace test2 {
+  namespace {
+    void foo(); // expected-warning {{function 'test2::<anonymous namespace>::foo' has internal linkage but is not defined}}
+    extern int var; // expected-warning {{variable 'test2::<anonymous namespace>::var' has internal linkage but is not defined}}
+    template <class T> void bar(); // expected-warning {{function 'test2::<anonymous namespace>::bar<int>' has internal linkage but is not defined}}
+  }
+  void test() {
+    foo(); // expected-note {{used here}}
+    var = 0; // expected-note {{used here}}
+    bar<int>(); // expected-note {{used here}}
+  }
+}
+
+namespace test3 {
+  namespace {
+    void foo();
+    extern int var;
+    template <class T> void bar();
+  }
+
+  void test() {
+    foo();
+    var = 0;
+    bar<int>();
+  }
+
+  namespace {
+    void foo() {}
+    int var = 0;
+    template <class T> void bar() {}
+  }
+}
+
+namespace test4 {
+  namespace {
+    struct A {
+      A(); // expected-warning {{function 'test4::<anonymous namespace>::A::A' has internal linkage but is not defined}}
+      ~A();// expected-warning {{function 'test4::<anonymous namespace>::A::~A' has internal linkage but is not defined}}
+      virtual void foo(); // expected-warning {{function 'test4::<anonymous namespace>::A::foo' has internal linkage but is not defined}}
+      virtual void bar() = 0;
+      virtual void baz(); // expected-warning {{function 'test4::<anonymous namespace>::A::baz' has internal linkage but is not defined}}
+    };
+  }
+
+  void test(A &a) {
+    a.foo(); // expected-note {{used here}}
+    a.bar();
+    a.baz(); // expected-note {{used here}}
+  }
+
+  struct Test : A {
+    Test() {} // expected-note 2 {{used here}}
+  };
+}
+
+// rdar://problem/9014651
+namespace test5 {
+  namespace {
+    struct A {};
+  }
+
+  template <class N> struct B {
+    static int var; // expected-warning {{variable 'test5::B<test5::<anonymous>::A>::var' has internal linkage but is not defined}}
+    static void foo(); // expected-warning {{function 'test5::B<test5::<anonymous>::A>::foo' has internal linkage but is not defined}}
+  };
+
+  void test() {
+    B<A>::var = 0; // expected-note {{used here}}
+    B<A>::foo(); // expected-note {{used here}}
+  }
+}