From 7fb213d8e955ed3f21defe0d28ab63fe576574bd Mon Sep 17 00:00:00 2001 From: Giovanni Bajo Date: Mon, 12 Jul 2004 10:07:30 +0000 Subject: re PR c++/2204 (G++ doesn't check (member) function parameter for abstract-ness.) PR c++/2204 * config-lang.in (gtfiles): Add typeck2.c. * Make-lang.in: Tweak typeck2.c dependencies, and add rule for gt-cp-typeck2.h. * cp-tree.h: Declare complete_type_check_abstract. * typeck2.c (pat_calc_hash, pat_compare, complete_type_check_abstract): New functions. (abstract_virtuals_error): If the type is abstract, register the declaration within abstract_pending_vars for further checks. Inspect also dependent types. Handle IDENTIFIER_NODEs as decl. * decl.c (cp_finish_decl): Do not strip array types. (create_array_type_for_decl): Check for abstractness of the element type. (complete_vars): Call complete_type_check_abstract. * class.c (finish_struct): Prepare a list of virtual functions for template types, and call complete_vars on it to check for abstractness. PR c++/2204 * g++.dg/other/abstract2.C: New test. From-SVN: r84552 --- gcc/cp/ChangeLog | 19 ++++++ gcc/cp/Make-lang.in | 4 +- gcc/cp/class.c | 16 +++++ gcc/cp/config-lang.in | 2 +- gcc/cp/cp-tree.h | 1 + gcc/cp/decl.c | 25 ++++---- gcc/cp/typeck2.c | 165 ++++++++++++++++++++++++++++++++++++++++++++++++-- 7 files changed, 210 insertions(+), 22 deletions(-) (limited to 'gcc/cp') diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 099b1bb..4b80aa6 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,22 @@ +2004-07-12 Giovanni Bajo + + PR c++/2204 + * config-lang.in (gtfiles): Add typeck2.c. + * Make-lang.in: Tweak typeck2.c dependencies, and add rule for + gt-cp-typeck2.h. + * cp-tree.h: Declare complete_type_check_abstract. + * typeck2.c (pat_calc_hash, pat_compare, + complete_type_check_abstract): New functions. + (abstract_virtuals_error): If the type is abstract, register the + declaration within abstract_pending_vars for further checks. + Inspect also dependent types. Handle IDENTIFIER_NODEs as decl. + * decl.c (cp_finish_decl): Do not strip array types. + (create_array_type_for_decl): Check for abstractness of the element + type. + (complete_vars): Call complete_type_check_abstract. + * class.c (finish_struct): Prepare a list of virtual functions for + template types, and call complete_vars on it to check for abstractness. + 2004-07-12 Paolo Bonzini PR tree-optimization/14107 diff --git a/gcc/cp/Make-lang.in b/gcc/cp/Make-lang.in index 864b51a..1ce05da 100644 --- a/gcc/cp/Make-lang.in +++ b/gcc/cp/Make-lang.in @@ -99,7 +99,7 @@ $(srcdir)/cp/cfns.h: $(srcdir)/cp/cfns.gperf gtype-cp.h gt-cp-call.h gt-cp-decl.h gt-cp-decl2.h : s-gtype; @true gt-cp-pt.h gt-cp-repo.h gt-cp-parser.h gt-cp-method.h : s-gtype; @true -gt-cp-tree.h gt-cp-mangle.h gt-cp-name-lookup.h: s-gtype; @true +gt-cp-tree.h gt-cp-mangle.h gt-cp-name-lookup.h gt-cp-typeck2.h: s-gtype; @true # # Build hooks: @@ -231,7 +231,7 @@ cp/decl.o: cp/decl.c $(CXX_TREE_H) $(TM_H) flags.h cp/decl.h stack.h \ cp/decl2.o: cp/decl2.c $(CXX_TREE_H) $(TM_H) flags.h cp/decl.h $(EXPR_H) \ output.h except.h toplev.h $(RTL_H) c-common.h gt-cp-decl2.h cgraph.h cp/typeck2.o: cp/typeck2.c $(CXX_TREE_H) $(TM_H) flags.h toplev.h output.h $(TM_P_H) \ - diagnostic.h + diagnostic.h gt-cp-typeck2.h cp/typeck.o: cp/typeck.c $(CXX_TREE_H) $(TM_H) flags.h $(RTL_H) $(EXPR_H) toplev.h \ diagnostic.h convert.h cp/class.o: cp/class.c $(CXX_TREE_H) $(TM_H) flags.h toplev.h $(RTL_H) $(TARGET_H) convert.h diff --git a/gcc/cp/class.c b/gcc/cp/class.c index f13ccac..080e5e9 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -5270,8 +5270,24 @@ finish_struct (tree t, tree attributes) if (processing_template_decl) { + tree x; + finish_struct_methods (t); TYPE_SIZE (t) = bitsize_zero_node; + + /* We need to emit an error message if this type was used as a parameter + and it is an abstract type, even if it is a template. We construct + a simple CLASSTYPE_PURE_VIRTUALS list without taking bases into + account and we call complete_vars with this type, which will check + the PARM_DECLS. Note that while the type is being defined, + CLASSTYPE_PURE_VIRTUALS contains the list of the inline friends + (see CLASSTYPE_INLINE_FRIENDS) so we need to clear it. */ + CLASSTYPE_PURE_VIRTUALS (t) = NULL_TREE; + for (x = TYPE_METHODS (t); x; x = TREE_CHAIN (x)) + if (DECL_PURE_VIRTUAL_P (x)) + CLASSTYPE_PURE_VIRTUALS (t) + = tree_cons (NULL_TREE, x, CLASSTYPE_PURE_VIRTUALS (t)); + complete_vars (t); } else finish_struct_1 (t); diff --git a/gcc/cp/config-lang.in b/gcc/cp/config-lang.in index 0d0c38f..563cfb4 100644 --- a/gcc/cp/config-lang.in +++ b/gcc/cp/config-lang.in @@ -34,4 +34,4 @@ stagestuff="g++\$(exeext) g++-cross\$(exeext) cc1plus\$(exeext)" target_libs="target-libstdc++-v3 target-gperf" -gtfiles="\$(srcdir)/cp/mangle.c \$(srcdir)/cp/name-lookup.h \$(srcdir)/cp/name-lookup.c \$(srcdir)/cp/cp-tree.h \$(srcdir)/cp/decl.h \$(srcdir)/cp/call.c \$(srcdir)/cp/decl.c \$(srcdir)/cp/decl2.c \$(srcdir)/cp/pt.c \$(srcdir)/cp/repo.c \$(srcdir)/cp/semantics.c \$(srcdir)/cp/tree.c \$(srcdir)/cp/parser.c \$(srcdir)/cp/method.c \$(srcdir)/c-common.c \$(srcdir)/c-common.h \$(srcdir)/c-lex.c \$(srcdir)/c-pragma.c" +gtfiles="\$(srcdir)/cp/mangle.c \$(srcdir)/cp/name-lookup.h \$(srcdir)/cp/name-lookup.c \$(srcdir)/cp/cp-tree.h \$(srcdir)/cp/decl.h \$(srcdir)/cp/call.c \$(srcdir)/cp/decl.c \$(srcdir)/cp/decl2.c \$(srcdir)/cp/pt.c \$(srcdir)/cp/repo.c \$(srcdir)/cp/semantics.c \$(srcdir)/cp/tree.c \$(srcdir)/cp/parser.c \$(srcdir)/cp/method.c \$(srcdir)/cp/typeck2.c \$(srcdir)/c-common.c \$(srcdir)/c-common.h \$(srcdir)/c-lex.c \$(srcdir)/c-pragma.c" diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 6cfe280..500ad11 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -4315,6 +4315,7 @@ extern void cxx_incomplete_type_error (tree, tree); extern tree error_not_base_type (tree, tree); extern tree binfo_or_else (tree, tree); extern void readonly_error (tree, const char *, int); +extern void complete_type_check_abstract (tree); extern int abstract_virtuals_error (tree, tree); extern tree store_init_value (tree, tree); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index ab5dd84..cc905eb 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -4821,21 +4821,12 @@ cp_finish_decl (tree decl, tree init, tree asmspec_tree, int flags) make_rtl_for_nonlocal_decl (decl, init, asmspec); + /* Check for abstractness of the type. Notice that there is no + need to strip array types here since the check for those types + is already done within create_array_type_for_decl. */ if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE) - abstract_virtuals_error (decl, - strip_array_types (TREE_TYPE (type))); - else if (POINTER_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE) - { - /* If it's either a pointer or an array type, strip through all - of them but the last one. If the last is an array type, issue - an error if the element type is abstract. */ - while (POINTER_TYPE_P (TREE_TYPE (type)) - || TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE) - type = TREE_TYPE (type); - if (TREE_CODE (type) == ARRAY_TYPE) - abstract_virtuals_error (decl, TREE_TYPE (type)); - } + abstract_virtuals_error (decl, TREE_TYPE (type)); else abstract_virtuals_error (decl, type); @@ -6162,6 +6153,11 @@ create_array_type_for_decl (tree name, tree type, tree size) if (size) itype = compute_array_index_type (name, size); + /* [dcl.array] + T is called the array element type; this type shall not be [...] an + abstract class type. */ + abstract_virtuals_error (name, type); + return build_cplus_array_type (type, itype); } @@ -10592,6 +10588,9 @@ complete_vars (tree type) else list = &TREE_CHAIN (*list); } + + /* Check for pending declarations which may have abstract type. */ + complete_type_check_abstract (type); } /* If DECL is of a type which needs a cleanup, build that cleanup diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c index 5ddfdd5..6e32377 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -114,6 +114,119 @@ readonly_error (tree arg, const char* string, int soft) (*fn) ("%s of read-only location", string); } + +/* Structure that holds information about declarations whose type was + incomplete and we could not check whether it was abstract or not. */ + +struct pending_abstract_type GTY((chain_next ("%h.next"))) +{ + /* Declaration which we are checking for abstractness. It is either + a DECL node, or an IDENTIFIER_NODE if we do not have a full + declaration available. */ + tree decl; + + /* Type which will be checked for abstractness. */ + tree type; + + /* Position of the declaration. This is only needed for IDENTIFIER_NODEs, + because DECLs already carry locus information. */ + location_t locus; + + /* Link to the next element in list. */ + struct pending_abstract_type* next; +}; + + +/* Compute the hash value of the node VAL. This function is used by the + hash table abstract_pending_vars. */ + +static hashval_t +pat_calc_hash (const void* val) +{ + const struct pending_abstract_type* pat = val; + return (hashval_t) TYPE_UID (pat->type); +} + + +/* Compare node VAL1 with the type VAL2. This function is used by the + hash table abstract_pending_vars. */ + +static int +pat_compare (const void* val1, const void* val2) +{ + const struct pending_abstract_type* pat1 = val1; + tree type2 = (tree)val2; + + return (pat1->type == type2); +} + +/* Hash table that maintains pending_abstract_type nodes, for which we still + need to check for type abstractness. The key of the table is the type + of the declaration. */ +static GTY ((param_is (struct pending_abstract_type))) +htab_t abstract_pending_vars = NULL; + + +/* This function is called after TYPE is completed, and will check if there + are pending declarations for which we still need to verify the abstractness + of TYPE, and emit a diagnostic (through abstract_virtuals_error) if TYPE + turned out to be incomplete. */ + +void +complete_type_check_abstract (tree type) +{ + void **slot; + struct pending_abstract_type *pat; + location_t cur_loc = input_location; + + my_friendly_assert (COMPLETE_TYPE_P (type), 20040620_3); + + if (!abstract_pending_vars) + return; + + /* Retrieve the list of pending declarations for this type. */ + slot = htab_find_slot_with_hash (abstract_pending_vars, type, + (hashval_t)TYPE_UID (type), NO_INSERT); + if (!slot) + return; + pat = (struct pending_abstract_type*)*slot; + my_friendly_assert (pat, 20040620_2); + + /* If the type is not abstract, do not do anything. */ + if (CLASSTYPE_PURE_VIRTUALS (type)) + { + struct pending_abstract_type *prev = 0, *next; + + /* Reverse the list to emit the errors in top-down order. */ + for (; pat; pat = next) + { + next = pat->next; + pat->next = prev; + prev = pat; + } + pat = prev; + + /* Go through the list, and call abstract_virtuals_error for each + element: it will issue a diagostic if the type is abstract. */ + while (pat) + { + my_friendly_assert (type == pat->type, 20040620_4); + + /* Tweak input_location so that the diagnostic appears at the correct + location. Notice that this is only needed if the decl is an + IDENTIFIER_NODE, otherwise cp_error_at. */ + input_location = pat->locus; + abstract_virtuals_error (pat->decl, pat->type); + pat = pat->next; + } + } + + htab_clear_slot (abstract_pending_vars, slot); + + input_location = cur_loc; +} + + /* If TYPE has abstract virtual functions, issue an error about trying to create an object of that type. DECL is the object declared, or NULL_TREE if the declaration is unavailable. Returns 1 if an error @@ -125,7 +238,45 @@ abstract_virtuals_error (tree decl, tree type) tree u; tree tu; - if (!CLASS_TYPE_P (type) || !CLASSTYPE_PURE_VIRTUALS (type)) + /* This function applies only to classes. Any other entity can never + be abstract. */ + if (!CLASS_TYPE_P (type)) + return 0; + + /* If the type is incomplete, we register it within a hash table, + so that we can check again once it is completed. This makes sense + only for objects for which we have a declaration or at least a + name. */ + if (!COMPLETE_TYPE_P (type)) + { + void **slot; + struct pending_abstract_type *pat; + + my_friendly_assert (!decl || (DECL_P (decl) + || TREE_CODE (decl) == IDENTIFIER_NODE), + 20040620_1); + + if (!abstract_pending_vars) + abstract_pending_vars = htab_create_ggc (31, &pat_calc_hash, + &pat_compare, NULL); + + slot = htab_find_slot_with_hash (abstract_pending_vars, type, + (hashval_t)TYPE_UID (type), INSERT); + + pat = ggc_alloc (sizeof (struct pending_abstract_type)); + pat->type = type; + pat->decl = decl; + pat->locus = ((decl && DECL_P (decl)) + ? DECL_SOURCE_LOCATION (decl) + : input_location); + + pat->next = *slot; + *slot = pat; + + return 0; + } + + if (!CLASSTYPE_PURE_VIRTUALS (type)) return 0; if (!TYPE_SIZE (type)) @@ -133,11 +284,6 @@ abstract_virtuals_error (tree decl, tree type) CLASSTYPE_PURE_VIRTUALS holds the inline friends. */ return 0; - if (dependent_type_p (type)) - /* For a dependent type, we do not yet know which functions are pure - virtuals. */ - return 0; - u = CLASSTYPE_PURE_VIRTUALS (type); if (decl) { @@ -160,6 +306,10 @@ abstract_virtuals_error (tree decl, tree type) else if (TREE_CODE (decl) == FUNCTION_DECL) cp_error_at ("invalid abstract return type for function `%+#D'", decl); + else if (TREE_CODE (decl) == IDENTIFIER_NODE) + /* Here we do not have location information, so use error instead + of cp_error_at. */ + error ("invalid abstract type `%T' for `%E'", type, decl); else cp_error_at ("invalid abstract type for `%+D'", decl); } @@ -1405,3 +1555,6 @@ require_complete_eh_spec_types (tree fntype, tree decl) } } } + + +#include "gt-cp-typeck2.h" -- cgit v1.1