Here we warn because i<l>::dispatch has internal linkage (because l does) and is never instantiated (because the vtable is never emitted). The regression happened because devirtualization started adding it to cgraph as a possible target. I think the way to fix this is to avoid adding an undefined internal function to cgraph as a possible target, since it is not, in fact, possible for it to be the actual target.

I think that the best place to fix this would be in can_refer_decl_in_current_unit_p, since the same reasoning applies to other possible references. But we could fix it only in gimple_get_virt_method_for_vtable.

First patch tested x86_64-pc-linux-gnu.

commit 3a02b58301c2c11620c2adc1aee4db1b7e8e36f2
Author: Jason Merrill <ja...@redhat.com>
Date:   Fri Jan 25 09:09:17 2019 -0500

            PR c++/80916 - spurious "static but not defined" warning.
    
            * gimple-fold.c (can_refer_decl_in_current_unit_p): Return false
            for an internal function with no definition.

diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 92d3fb4a9e0..20564e26de1 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -135,6 +135,12 @@ can_refer_decl_in_current_unit_p (tree decl, tree from_decl)
       return !node || !node->global.inlined_to;
     }
 
+  /* This function is internal and not defined, so nothing can refer to it.  */
+  if (!TREE_PUBLIC (decl) && DECL_EXTERNAL (decl)
+      && TREE_CODE (decl) == FUNCTION_DECL
+      && DECL_INITIAL (decl) == NULL_TREE)
+    return false;
+
   /* We will later output the initializer, so we can refer to it.
      So we are concerned only when DECL comes from initializer of
      external var or var that has been optimized out.  */
diff --git a/gcc/testsuite/g++.dg/warn/unused-fn1.C b/gcc/testsuite/g++.dg/warn/unused-fn1.C
new file mode 100644
index 00000000000..aabc01b3f44
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/unused-fn1.C
@@ -0,0 +1,16 @@
+// PR c++/80916
+// { dg-options "-Os -Wunused" }
+
+struct j {
+  virtual void dispatch(void *) {}
+};
+template <typename>
+struct i : j {
+  void dispatch(void *) {} // warning: 'void i< <template-parameter-1-1> >::dispatch(void*) [with <template-parameter-1-1> = {anonymous}::l]' declared 'static' but never defined [-Wunused-function]
+};
+namespace {
+  struct l : i<l> {};
+}
+void f(j *k) {
+  k->dispatch(0);
+}
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 92d3fb4a9e0..8d63d815a5e 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -7146,7 +7146,10 @@ gimple_get_virt_method_for_vtable (HOST_WIDE_INT token,
 	 devirtualize.  This can happen in WHOPR when the actual method
 	 ends up in other partition, because we found devirtualization
 	 possibility too late.  */
-      if (!can_refer_decl_in_current_unit_p (fn, vtable))
+      if (!can_refer_decl_in_current_unit_p (fn, vtable)
+	  || (!TREE_PUBLIC (decl) && DECL_EXTERNAL (decl)
+	      && TREE_CODE (decl) == FUNCTION_DECL
+	      && DECL_INITIAL (decl) == NULL_TREE))
 	{
 	  if (can_refer)
 	    {

Reply via email to