diff mbox

add __is_final trait to fix libstdc++/51365

Message ID CAH6eHdQTk_PP01oMOskmhV0sA0NKwdArAn5owigK9BJTjitOyg@mail.gmail.com
State New
Headers show

Commit Message

Jonathan Wakely Dec. 3, 2011, 12:04 p.m. UTC
On 3 December 2011 12:00, Jonathan Wakely wrote:
> This implements a new C++ trait, __is_final, to query the 'final'
> specifier that 4.7 supports. The trait is needed for the library so we
> can detect when it's not possible to derive from a type in order to
> exploit the empty base-class optimisation.  This affects all
> containers, std::shared_ptr, std::future, std::tuple and anywhere else
> that tries to use the EBO.  It's not a C++11-only problem because we
> support __final in c++98 mode.  See PR libstdc++/51365 for examples of
> programs using final classes that cause problems in libstdc++ and for
> patches to the library that use this trait to solve the problems.
>
> I originally thought about naming this __is_final_class, which would
> allow a possible __is_final_function in future, but I think
> __is_final(type) is pretty clear.  There is a proposal to add
> __is_final to Clang with the same spelling and semantics.
>
> Tested x86_64-linux, ok for trunk?
>
> c-family/ChangeLog:
>
>        * c-common.c (RID_IS_FINAL): Add.
>        * c-common.h (RID_IS_FINAL): Add.
>
> cp/ChangeLog:
>
>        * cp-tree.h (CPTK_IS_FINAL): Add.
>        * parser.c (cp_parser_translation_unit): Handle RID_IS_FINAL.
>        (cp_parser_primary_expression, cp_parser_trait_expr): Likewise.
>        * semantics.c (trait_expr_value, finish_trait_expr): Handle
>        CPTK_IS_FINAL.
>        * cxx-pretty-print.c (pp_cxx_trait_expression): Likewise.
>
> ChangeLog:
>
>        * doc/extend.texi (Type Traits): Add __is_final.
>
> testsuite/ChangeLog:
>
>        * g++.dg/ext/is_final.C: New.


Oops, there was a stray chunk in that patch:

@@ -4923,6 +4924,7 @@ extern bool user_provided_p			(tree);
 extern bool type_has_user_provided_constructor  (tree);
 extern bool type_has_user_provided_default_constructor (tree);
 extern tree default_init_uninitialized_part (tree);
+extern void warn_missing_meminits (tree, tree);
 extern bool trivial_default_constructor_is_constexpr (tree);
 extern bool type_has_constexpr_default_constructor (tree);
 extern bool type_has_virtual_destructor		(tree);

Here's the correct patch (identical except for removing that piece)
diff mbox

Patch

Index: c-family/c-common.c
===================================================================
--- c-family/c-common.c	(revision 181967)
+++ c-family/c-common.c	(working copy)
@@ -462,6 +462,7 @@  const struct c_common_resword c_common_r
   { "__is_convertible_to", RID_IS_CONVERTIBLE_TO, D_CXXONLY },
   { "__is_empty",	RID_IS_EMPTY,	D_CXXONLY },
   { "__is_enum",	RID_IS_ENUM,	D_CXXONLY },
+  { "__is_final",	RID_IS_FINAL,	D_CXXONLY },
   { "__is_literal_type", RID_IS_LITERAL_TYPE, D_CXXONLY },
   { "__is_pod",		RID_IS_POD,	D_CXXONLY },
   { "__is_polymorphic",	RID_IS_POLYMORPHIC, D_CXXONLY },
Index: c-family/c-common.h
===================================================================
--- c-family/c-common.h	(revision 181967)
+++ c-family/c-common.h	(working copy)
@@ -134,7 +134,7 @@  enum rid
   RID_CONSTCAST, RID_DYNCAST, RID_REINTCAST, RID_STATCAST,
 
   /* C++ extensions */
-  RID_BASES,                  RID_DIRECT_BASES,
+  RID_BASES,                   RID_DIRECT_BASES,
   RID_HAS_NOTHROW_ASSIGN,      RID_HAS_NOTHROW_CONSTRUCTOR,
   RID_HAS_NOTHROW_COPY,        RID_HAS_TRIVIAL_ASSIGN,
   RID_HAS_TRIVIAL_CONSTRUCTOR, RID_HAS_TRIVIAL_COPY,
@@ -142,12 +142,12 @@  enum rid
   RID_IS_ABSTRACT,             RID_IS_BASE_OF,
   RID_IS_CLASS,                RID_IS_CONVERTIBLE_TO,
   RID_IS_EMPTY,                RID_IS_ENUM,
-  RID_IS_LITERAL_TYPE,         RID_IS_POD,
-  RID_IS_POLYMORPHIC,          RID_IS_STD_LAYOUT,
-  RID_IS_TRIVIAL,              RID_IS_UNION,
-  RID_UNDERLYING_TYPE,
+  RID_IS_FINAL,                RID_IS_LITERAL_TYPE,
+  RID_IS_POD,                  RID_IS_POLYMORPHIC,
+  RID_IS_STD_LAYOUT,           RID_IS_TRIVIAL,
+  RID_IS_UNION,                RID_UNDERLYING_TYPE,
 
-  /* C++0x */
+  /* C++11 */
   RID_CONSTEXPR, RID_DECLTYPE, RID_NOEXCEPT, RID_NULLPTR, RID_STATIC_ASSERT,
 
   /* Objective-C ("AT" reserved words - they are only keywords when
Index: cp/cp-tree.h
===================================================================
--- cp/cp-tree.h	(revision 181967)
+++ cp/cp-tree.h	(working copy)
@@ -584,6 +584,7 @@  typedef enum cp_trait_kind
   CPTK_IS_CONVERTIBLE_TO,
   CPTK_IS_EMPTY,
   CPTK_IS_ENUM,
+  CPTK_IS_FINAL,
   CPTK_IS_LITERAL_TYPE,
   CPTK_IS_POD,
   CPTK_IS_POLYMORPHIC,
Index: cp/parser.c
===================================================================
--- cp/parser.c	(revision 181967)
+++ cp/parser.c	(working copy)
@@ -3854,6 +3854,7 @@  cp_parser_translation_unit (cp_parser* p
      __is_convertible_to ( type-id , type-id )     
      __is_empty ( type-id )
      __is_enum ( type-id )
+     __is_final ( type-id )
      __is_literal_type ( type-id )
      __is_pod ( type-id )
      __is_polymorphic ( type-id )
@@ -4199,6 +4200,7 @@  cp_parser_primary_expression (cp_parser
 	case RID_IS_CONVERTIBLE_TO:
 	case RID_IS_EMPTY:
 	case RID_IS_ENUM:
+	case RID_IS_FINAL:
 	case RID_IS_LITERAL_TYPE:
 	case RID_IS_POD:
 	case RID_IS_POLYMORPHIC:
@@ -7868,6 +7870,9 @@  cp_parser_trait_expr (cp_parser* parser,
     case RID_IS_ENUM:
       kind = CPTK_IS_ENUM;
       break;
+    case RID_IS_FINAL:
+      kind = CPTK_IS_FINAL;
+      break;
     case RID_IS_LITERAL_TYPE:
       kind = CPTK_IS_LITERAL_TYPE;
       break;
Index: cp/semantics.c
===================================================================
--- cp/semantics.c	(revision 181967)
+++ cp/semantics.c	(working copy)
@@ -5412,6 +5412,9 @@  trait_expr_value (cp_trait_kind kind, tr
     case CPTK_IS_ENUM:
       return (type_code1 == ENUMERAL_TYPE);
 
+    case CPTK_IS_FINAL:
+      return (CLASS_TYPE_P (type1) && CLASSTYPE_FINAL (type1));
+
     case CPTK_IS_LITERAL_TYPE:
       return (literal_type_p (type1));
 
@@ -5471,6 +5474,7 @@  finish_trait_expr (cp_trait_kind kind, t
 	      || kind == CPTK_IS_CONVERTIBLE_TO
 	      || kind == CPTK_IS_EMPTY
 	      || kind == CPTK_IS_ENUM
+	      || kind == CPTK_IS_FINAL
 	      || kind == CPTK_IS_LITERAL_TYPE
 	      || kind == CPTK_IS_POD
 	      || kind == CPTK_IS_POLYMORPHIC
@@ -5511,6 +5515,7 @@  finish_trait_expr (cp_trait_kind kind, t
     case CPTK_HAS_VIRTUAL_DESTRUCTOR:
     case CPTK_IS_ABSTRACT:
     case CPTK_IS_EMPTY:
+    case CPTK_IS_FINAL:
     case CPTK_IS_LITERAL_TYPE:
     case CPTK_IS_POD:
     case CPTK_IS_POLYMORPHIC:
Index: cp/cxx-pretty-print.c
===================================================================
--- cp/cxx-pretty-print.c	(revision 181967)
+++ cp/cxx-pretty-print.c	(working copy)
@@ -2380,6 +2380,9 @@  pp_cxx_trait_expression (cxx_pretty_prin
     case CPTK_IS_ENUM:
       pp_cxx_ws_string (pp, "__is_enum");
       break;
+    case CPTK_IS_FINAL:
+      pp_cxx_ws_string (pp, "__is_final");
+      break;
     case CPTK_IS_POD:
       pp_cxx_ws_string (pp, "__is_pod");
       break;
Index: doc/extend.texi
===================================================================
--- doc/extend.texi	(revision 181967)
+++ doc/extend.texi	(working copy)
@@ -15164,6 +15164,10 @@  of unknown bound.
 If @code{type} is a cv enumeration type ([basic.compound]) the trait is
 true, else it is false.
 
+@item __is_final (type)
+If @code{type} is a cv class type marked @code{final} ([class]) the trait
+is true, else it is false.
+
 @item __is_literal_type (type)
 If @code{type} is a literal type ([basic.types]) the trait is
 true, else it is false.  Requires: @code{type} shall be a complete type,
Index: testsuite/g++.dg/ext/is_final.C
===================================================================
--- testsuite/g++.dg/ext/is_final.C	(revision 0)
+++ testsuite/g++.dg/ext/is_final.C	(revision 0)
@@ -0,0 +1,46 @@ 
+// PR c++/51365
+// { dg-do compile }
+// { dg-options "-std=c++0x" }
+
+struct A { };
+static_assert( ! __is_final (A), "A not final" );
+
+struct Af final { };
+static_assert( __is_final (Af), "Af is final" );
+
+class B { };
+static_assert( ! __is_final (B), "B not final" );
+
+class Bf final { };
+static_assert( __is_final (Bf), "Bf is final" );
+
+struct C : private A, private B { };
+static_assert( ! __is_final (C), "C not final" );
+
+struct Cf final : private A, private B { };
+static_assert( __is_final (Cf), "Cf is final" );
+
+struct D { virtual ~D() final { } };
+static_assert( ! __is_final (D), "D not final" );
+
+struct Df final { virtual ~Df() final { } };
+static_assert( __is_final (Df), "Df is final" );
+
+template<typename> struct E { };
+static_assert( ! __is_final (E<int>), "E<int> not final" );
+static_assert( ! __is_final (E<Af>),  "E<Af> not final" );
+
+template<typename> struct Ef final { };
+static_assert( __is_final (Ef<int>), "Ef<int> is final" );
+static_assert( __is_final (Ef<A>),   "Ef<A> is final" );
+static_assert( __is_final (Ef<Af>),  "Ef<Af> is final" );
+
+template<typename> struct F { virtual ~F() final { }; };
+static_assert( ! __is_final (F<int>), "F<int> not final" );
+static_assert( ! __is_final (F<Af>),  "F<Af> not final" );
+
+template<typename> struct Ff final { virtual ~Ff() final { }; };
+static_assert( __is_final (Ff<int>), "Ff<int> is final" );
+static_assert( __is_final (Ff<A>),   "Ff<A> is final" );
+static_assert( __is_final (Ff<Af>),  "Ff<Af> is final" );
+