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