At Sat, 14 May 2011 09:01:39 +0200, Paolo Carlini wrote: > ... I'm wondering if wouldn't be more appropriate for the new testcase to be > in /cpp0x, with a name like final.C
There are probably other tests there that need moving too, if such moving is done. I don't have a strong opinion either way, but maybe the moving should be done separately? The last patch contained a ligature in the grammar comment for class-virt-specifier.. fixed below. diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 293dd1c..40a7643 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -1268,6 +1268,10 @@ check_bases (tree t, gcc_assert (COMPLETE_TYPE_P (basetype)); + if (CLASSTYPE_FINAL (basetype)) + error ("cannot derive from %<final%> base %qT in derived type %qT", + basetype, t); + /* If any base class is non-literal, so is the derived class. */ if (!CLASSTYPE_LITERAL_P (basetype)) CLASSTYPE_LITERAL_P (t) = false; diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 1705232..901a17d 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -1321,6 +1321,7 @@ struct GTY(()) lang_type_class { unsigned has_complex_move_ctor : 1; unsigned has_complex_move_assign : 1; unsigned has_constexpr_ctor : 1; + unsigned is_final : 1; /* When adding a flag here, consider whether or not it ought to apply to a template instance if it applies to the template. If @@ -1329,7 +1330,7 @@ struct GTY(()) lang_type_class { /* There are some bits left to fill out a 32-bit word. Keep track of this by updating the size of this bitfield whenever you add or remove a flag. */ - unsigned dummy : 3; + unsigned dummy : 2; tree primary_base; VEC(tree_pair_s,gc) *vcall_indices; @@ -1437,6 +1438,11 @@ struct GTY((variable_size)) lang_type { #define CLASSTYPE_LAZY_DESTRUCTOR(NODE) \ (LANG_TYPE_CLASS_CHECK (NODE)->lazy_destructor) +/* Nonzero means that NODE (a class type) is final */ +#define CLASSTYPE_FINAL(NODE) \ + (LANG_TYPE_CLASS_CHECK (NODE)->is_final) + + /* Nonzero means that this _CLASSTYPE node overloads operator=(X&). */ #define TYPE_HAS_COPY_ASSIGN(NODE) (LANG_TYPE_CLASS_CHECK (NODE)->has_copy_assign) diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index fa6cd83..a77d4ff 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -17082,10 +17082,13 @@ cp_parser_class_specifier (cp_parser* parser) class-head: class-key identifier [opt] base-clause [opt] - class-key nested-name-specifier identifier base-clause [opt] + class-key nested-name-specifier identifier class-virt-specifier [opt] base-clause [opt] class-key nested-name-specifier [opt] template-id base-clause [opt] + class-virt-specifier: + final + GNU Extensions: class-key attributes identifier [opt] base-clause [opt] class-key attributes nested-name-specifier identifier base-clause [opt] @@ -17117,6 +17120,7 @@ cp_parser_class_head (cp_parser* parser, tree id = NULL_TREE; tree type = NULL_TREE; tree attributes; + cp_virt_specifiers virt_specifiers = VIRT_SPEC_UNSPECIFIED; bool template_id_p = false; bool qualified_p = false; bool invalid_nested_name_p = false; @@ -17260,8 +17264,11 @@ cp_parser_class_head (cp_parser* parser, pop_deferring_access_checks (); if (id) - cp_parser_check_for_invalid_template_id (parser, id, - type_start_token->location); + { + cp_parser_check_for_invalid_template_id (parser, id, + type_start_token->location); + virt_specifiers = cp_parser_virt_specifier_seq_opt (parser); + } /* If it's not a `:' or a `{' then we can't really be looking at a class-head, since a class-head only appears as part of a @@ -17277,6 +17284,13 @@ cp_parser_class_head (cp_parser* parser, /* At this point, we're going ahead with the class-specifier, even if some other problem occurs. */ cp_parser_commit_to_tentative_parse (parser); + if (virt_specifiers & VIRT_SPEC_OVERRIDE) + { + cp_parser_error (parser, + "cannot specify %<override%> for a class"); + type = error_mark_node; + goto out; + } /* Issue the error about the overly-qualified name now. */ if (qualified_p) { @@ -17493,6 +17507,8 @@ cp_parser_class_head (cp_parser* parser, if (type) DECL_SOURCE_LOCATION (TYPE_NAME (type)) = type_start_token->location; *attributes_p = attributes; + if (type && (virt_specifiers & VIRT_SPEC_FINAL)) + CLASSTYPE_FINAL (type) = 1; out: parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p; return type; diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 5e24977..5e059e0 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -8209,6 +8209,7 @@ instantiate_class_template_1 (tree type) CLASSTYPE_VISIBILITY_SPECIFIED (type) = 1; CLASSTYPE_VISIBILITY (type) = CLASSTYPE_VISIBILITY (pattern); } + CLASSTYPE_FINAL (type) = CLASSTYPE_FINAL (pattern); pbinfo = TYPE_BINFO (pattern); diff --git a/gcc/testsuite/g++.dg/inherit/base4.C b/gcc/testsuite/g++.dg/inherit/base4.C new file mode 100644 index 0000000..7f17504 --- /dev/null +++ b/gcc/testsuite/g++.dg/inherit/base4.C @@ -0,0 +1,47 @@ +// { dg-do compile } +// { dg-options "--std=c++0x" } +struct B1 {}; + +struct B2 final {}; + +struct D1 : B1 {}; + +struct D2 : B2 {}; // { dg-error "cannot derive from 'final' base" } + +template<class T> struct D3 : T {}; + +template<class T> struct D4 : T {}; // { dg-error "cannot derive from 'final' base" } + +template <class T> struct B3 {}; + +template <class T> struct B4 final {}; + +template <class T> struct B5 final {}; + +struct undeclared<int> final { }; // { dg-error "not a template" } + +struct D5 : B3<D5> {}; + +struct D6 : B4<D6> {}; // { dg-error "cannot derive from 'final' base" } + +struct B6 final final {}; // { dg-error "duplicate virt-specifier" } + +struct B7 override {}; // { dg-error "cannot specify 'override' for a class" } + +int main() +{ + D3<B1> d; + D4<B2> d2; + struct B2 final{}; // { dg-error "previous definition" } + B2 final; // { dg-error "has a previous declaration|previously declared here" } + B2 final2 = final; + struct B2 {}; // { dg-error "redefinition" } + struct B2 final; // { dg-error "redeclaration" } + struct B2 override; // { dg-error "previously declared here" } + struct B2 final {}; // { dg-error "redefinition" } + struct B2 override {}; // { dg-error "cannot specify 'override' for a class" } + B2 override{}; // { dg-error "redeclaration" } + struct foo final {}; // { dg-error "previous definition" } + struct foo final {}; // { dg-error "redefinition" } + foo final; // { dg-error "conflicting declaration" } +}