PATCH: Implement __attribute__((final)) for C++, restricting subtyping and overriding

Jeffrey Yasskin jyasskin@gmail.com
Fri Oct 10 18:26:00 GMT 2008


Hi all.

I've implemented __attribute__ ((final)) for C++ classes and virtual
member functions. It behaves very similarly to Java's final keyword:
when applied to a class, it prevents anyone from subtyping that class;
and when applied to a virtual member function, it prevents any
subtypes from overriding that function. The patch shouldn't affect any
code that doesn't use the attribute. In theory, knowing that a method
is the final override could allow g++ to, in some cases, emit static
calls instead of virtual calls, but I haven't implemented that here.

In the existing language, it's possible to use virtual inheritance to
make a class final (see
http://www.codeguru.com/cpp/cpp/cpp_mfc/stl/article.php/c4143), but
this introduces some runtime overhead and has worse error messages.
The need for individual member functions to be final can often be
obviated by using a separate strategy object, but this again
introduces a small runtime cost and may not be the class organization
the programmer really wants. Neither existing possibility can help the
compiler optimize.

I understand that gcc-4.4 is in stage 3 and so this can't go in until
gcc-4.5, but I wanted to get it out there so I don't miss 4.5 too.

This is bootstrapped and tested (with make check-c++) on
i386-apple-darwin9.5.0. The failures match
http://gcc.gnu.org/ml/gcc-testresults/2008-10/msg00521.html.

This is my first change to gcc, so sorry if I've made any obvious
mistakes. Let me know what you think.

2008-10-10  Jeffrey Yasskin  <jyasskin@google.com>

gcc/cp/ChangeLog:
        * tree.c (cxx_attribute_table): Define final attribute ...
        (handle_final_attribute): ... here.
        * class.c (check_for_override): Warn when final applied to
non-virtual member or virtual member that doesn't override anything.
        * decl.c (xref_basetypes): Enforce final on base types.
        * search.c (check_final_overrider): Enforce final on virtual
member functions.

gcc/testsuite/ChangeLog:
        * g++.dg/inherit/final1.C: New test.
        * g++.dg/inherit/final2.C: New test.

gcc/ChangeLog:
	* doc/extend.texi: Document final attribute.


Index: gcc/doc/extend.texi
===================================================================
--- gcc/doc/extend.texi	(revision 140963)
+++ gcc/doc/extend.texi	(working copy)
@@ -12758,6 +12758,49 @@ You must specify @option{-Wno-pmf-conversions} to
 Some attributes only make sense for C++ programs.

 @table @code
+@item final
+@cindex final attribute
+
+The @code{final} attribute declares that a class cannot be derived
+from, or that a virtual member function cannot be overridden.  In the
+future, the compiler might use this guarantee to emit static calls
+instead of virtual calls when it has enough information about the
+static type of an object, but for now it's just a semantic
+restriction.
+
+On classes:
+
+@smallexample
+class __attribute__ ((final)) Base
+@{
+  ...
+@};
+class Derived : public Base  // error
+@{
+  ...
+@};
+@end smallexample
+
+And on member functions:
+
+@smallexample
+class Base
+@{
+  virtual void foo() = 0;
+  virtual void bar() = 0;
+@};
+class Derived1 : public Base
+@{
+  virtual __attribute__ ((final)) void foo();
+  virtual void bar()  __attribute__ ((final));
+@};
+class Derived2 : public Derived1
+@{
+  virtual void foo();  // error
+  virtual void bar();  // error
+@};
+@end smallexample
+
 @item init_priority (@var{priority})
 @cindex init_priority attribute

Index: gcc/testsuite/g++.dg/inherit/final2.C
===================================================================
--- gcc/testsuite/g++.dg/inherit/final2.C	(revision 0)
+++ gcc/testsuite/g++.dg/inherit/final2.C	(revision 0)
@@ -0,0 +1,56 @@
+// { dg-do compile }
+
+class __attribute__ ((final)) FinalBase
+{  // { dg-error "derived from" }
+};
+
+class BadDerived1  // { dg-error "cannot derive" }
+  : public FinalBase
+{};
+
+class FinalPolymorphicBase
+{  // { dg-error "derived from" }
+  virtual ~FinalPolymorphicBase() {}
+} __attribute__ ((final));
+
+class BadDerived2  // { dg-error "cannot derive" }
+  : public FinalPolymorphicBase
+{};
+
+class Base {};
+
+class FinalDerived : public Base
+{  // { dg-error "derived from" }
+} __attribute__ ((final));
+
+class BadDerived3  // { dg-error "cannot derive" }
+  : public FinalDerived
+{};
+
+class PolymorphicBase
+{
+  virtual ~PolymorphicBase() {}
+};
+
+class FinalDerivedFromPolymorphic
+  : public PolymorphicBase
+{  // { dg-error "derived from" }
+} __attribute__ ((final));
+
+class BadDerived4  // { dg-error "cannot derive" }
+  : public FinalDerivedFromPolymorphic
+{
+};
+
+template<typename T>
+class DeriveFrom : public T
+{};  // { dg-error "cannot derive" }
+
+DeriveFrom<FinalBase> bad_instantiation;
+
+typedef Base BadTypedef __attribute__ ((final));  // { dg-warning
"can only be applied" }
+
+class __attribute__ ((final)) FinalToBeTypedefed {};  // { dg-error
"derived from" }
+typedef FinalToBeTypedefed TypedefedFinalBase;
+class BadDerived5  // { dg-error "cannot derive" }
+  : public TypedefedFinalBase {};
Index: gcc/testsuite/g++.dg/inherit/final1.C
===================================================================
--- gcc/testsuite/g++.dg/inherit/final1.C	(revision 0)
+++ gcc/testsuite/g++.dg/inherit/final1.C	(revision 0)
@@ -0,0 +1,76 @@
+// { dg-do compile }
+
+void global_func() __attribute__ ((final));  /* { dg-warning "can
only be applied" } */
+
+class NonVirtualTest
+{
+  void nonvirtual() __attribute__ ((final));  /* { dg-warning "has no
effect" } */
+  static void static_member() __attribute__ ((final));  /* {
dg-warning "can only be applied" } */
+};
+class GoodDerivedNonVirtualTest : public NonVirtualTest
+{
+  void nonvirtual();  // No warning.
+};
+
+class Base
+{
+  virtual void foo();
+  virtual void bar();
+};
+
+class Derived1 : public Base
+{
+  virtual void foo() __attribute__ ((final));  // { dg-error "overriding" }
+};
+
+class SubDerived1Bad : public Derived1
+{
+  virtual void foo();  // { dg-error "cannot override final function" }
+};
+
+class SubDerived1Good : public Derived1
+{
+  virtual void baz();
+};
+
+// The same method can be final in one sub-tree, and not final in another.
+class Derived2 : public Base
+{
+  virtual void foo();
+};
+class SubDerived2 : public Derived2
+{
+  virtual void foo();
+};
+
+// Skipping a level of the class hierarchy doesn't avoid the error.
+class GrandChild : public Derived1 {};
+class GreatGrandChild : public GrandChild
+{
+  virtual void foo();  // { dg-error "cannot override final function" }
+};
+
+class ImplicitVirtual : public Base
+{
+  void foo() __attribute__ ((final));  // Doesn't error.
+};
+
+
+// Warn when the initial virtual function is also final, since then it
+// may as well just not be virtual.
+class InitialVirtualFinal {
+  virtual void bar() __attribute__ ((final));  // { dg-warning "does
not override anything" }
+};
+
+
+// Put the attribute before the type too.
+class BeforeType : public Base
+{
+  virtual __attribute__ ((final)) void foo();  // { dg-error "overriding" }
+  __attribute__ ((final)) virtual void bar();  // { dg-error "overriding" }
+};
+class DerivedFromBeforeType : public BeforeType
+{
+  virtual void foo();  // { dg-error "cannot override final function" }
+  virtual void bar();  // { dg-error "cannot override final function" }
+};
Index: gcc/cp/class.c
===================================================================
--- gcc/cp/class.c	(revision 140963)
+++ gcc/cp/class.c	(working copy)
@@ -2382,9 +2382,18 @@ check_for_override (tree decl, tree ctype)
   if (DECL_VIRTUAL_P (decl))
     {
       if (!DECL_VINDEX (decl))
-	DECL_VINDEX (decl) = error_mark_node;
+        {
+          DECL_VINDEX (decl) = error_mark_node;
+          if (lookup_attribute ("final", DECL_ATTRIBUTES (decl)))
+            warning (OPT_Wattributes, "final member function %q+D does not"
+                     " override anything; consider making it non-virtual"
+                     " instead", decl);
+        }
       IDENTIFIER_VIRTUAL_P (DECL_NAME (decl)) = 1;
     }
+  else if (lookup_attribute ("final", DECL_ATTRIBUTES (decl)))
+    warning (OPT_Wattributes, "%<final%> attribute has no effect when applied"
+             " to non-virtual member function %q+#D", decl);
 }

 /* Warn about hidden virtual functions that are not overridden in t.
Index: gcc/cp/decl.c
===================================================================
--- gcc/cp/decl.c	(revision 140963)
+++ gcc/cp/decl.c	(working copy)
@@ -10720,6 +10720,14 @@ xref_basetypes (tree ref, tree base_list)
 	  return false;
 	}

+      if (CLASS_TYPE_P (basetype)
+          && lookup_attribute ("final", TYPE_ATTRIBUTES (basetype)))
+        {
+          error ("cannot derive %q+T from final class", ref);
+          error ("  derived from %q+T", basetype);
+          /* Continue processing anyway. */
+        }
+
       if (TYPE_FOR_JAVA (basetype) && (current_lang_depth () == 0))
 	TYPE_FOR_JAVA (ref) = 1;

Index: gcc/cp/tree.c
===================================================================
--- gcc/cp/tree.c	(revision 140963)
+++ gcc/cp/tree.c	(working copy)
@@ -53,7 +53,9 @@ static tree build_local_temp (tree);
 static tree handle_java_interface_attribute (tree *, tree, tree, int, bool *);
 static tree handle_com_interface_attribute (tree *, tree, tree, int, bool *);
 static tree handle_init_priority_attribute (tree *, tree, tree, int, bool *);
+static tree handle_final_attribute (tree *, tree, tree, int, bool *);

+
 /* If REF is an lvalue, returns the kind of lvalue that REF is.
    Otherwise, returns clk_none.  If TREAT_CLASS_RVALUES_AS_LVALUES is
    nonzero, rvalues of class type are considered lvalues.  */
@@ -2165,6 +2167,7 @@ const struct attribute_spec cxx_attribute_table[]
   { "java_interface", 0, 0, false, false, false,
handle_java_interface_attribute },
   { "com_interface",  0, 0, false, false, false,
handle_com_interface_attribute },
   { "init_priority",  1, 1, true,  false, false,
handle_init_priority_attribute },
+  { "final",          0, 0, false, false, false, handle_final_attribute },
   { NULL,	      0, 0, false, false, false, NULL }
 };

@@ -2296,6 +2299,31 @@ handle_init_priority_attribute (tree* node,
     }
 }

+/* Handle a "final" attribute; arguments as in
+   struct attribute_spec.handler.  */
+static tree
+handle_final_attribute (tree* node,
+                        tree name,
+                        tree args ATTRIBUTE_UNUSED,
+                        int flags ATTRIBUTE_UNUSED,
+                        bool* no_add_attrs)
+{
+  if ((TREE_CODE (*node) == FUNCTION_DECL
+       && DECL_NONSTATIC_MEMBER_FUNCTION_P (*node))
+      || TREE_CODE (*node) == RECORD_TYPE)
+    ;  /* Leave the "final" attribute in the DECL_ATTRIBUTES or
+          TYPE_ATTRIBUTES list to look up later. Ideally, we'd also
+          check that the function is virtual here, but we only find
+          that out in check_for_override(), so check there instead. */
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute can only be applied to structs,"
+               " classes, and virtual member functions", name);
+      *no_add_attrs = true;
+    }
+  return NULL_TREE;
+}
+
 /* Return a new PTRMEM_CST of the indicated TYPE.  The MEMBER is the
    thing pointed to by the constant.  */

Index: gcc/cp/search.c
===================================================================
--- gcc/cp/search.c	(revision 140963)
+++ gcc/cp/search.c	(working copy)
@@ -1926,6 +1926,14 @@ check_final_overrider (tree overrider, tree basefn
 	}
       return 0;
     }
+
+  if (lookup_attribute ("final", DECL_ATTRIBUTES (basefn)))
+    {
+      error ("%q+D cannot override final function", overrider);
+      error ("  overriding %q+#D", basefn);
+      DECL_INVALID_OVERRIDER_P (overrider) = 1;
+      return 0;
+    }
   return 1;
 }



More information about the Gcc-patches mailing list